cycls 0.0.2.102__tar.gz → 0.0.2.104__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (21) hide show
  1. {cycls-0.0.2.102 → cycls-0.0.2.104}/PKG-INFO +3 -3
  2. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/app.py +2 -4
  3. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/function.py +3 -0
  4. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/web.py +26 -21
  5. {cycls-0.0.2.102 → cycls-0.0.2.104}/pyproject.toml +4 -4
  6. cycls-0.0.2.102/cycls/state.py +0 -6
  7. {cycls-0.0.2.102 → cycls-0.0.2.104}/.gitignore +0 -0
  8. {cycls-0.0.2.102 → cycls-0.0.2.104}/README.md +0 -0
  9. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/__init__.py +0 -0
  10. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/auth.py +0 -0
  11. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/cli.py +0 -0
  12. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/FileSaver.min-BN40akLc.js +0 -0
  13. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/html2canvas.esm-QH1iLAAe.js +0 -0
  14. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/index-3AQCbjkB.js +0 -0
  15. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/index-CjN0qc53.css +0 -0
  16. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/index-qcHL6SRo.js +0 -0
  17. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/index.es-BmxgotDY.js +0 -0
  18. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/jspdf.es.min-CvOy4co6.js +0 -0
  19. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/assets/purify.es-B9ZVCkUG.js +0 -0
  20. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/default/index.html +0 -0
  21. {cycls-0.0.2.102 → cycls-0.0.2.104}/cycls/themes/dev/index.html +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycls
3
- Version: 0.0.2.102
3
+ Version: 0.0.2.104
4
4
  Summary: Distribute Intelligence
5
5
  Author-email: "Mohammed J. AlRujayi" <mj@cycls.com>
6
6
  Requires-Python: >=3.10
@@ -10,10 +10,10 @@ Requires-Dist: email-validator>=2.0.0
10
10
  Requires-Dist: fastapi>=0.111.0
11
11
  Requires-Dist: httpx>=0.27.0
12
12
  Requires-Dist: pyjwt>=2.8.0
13
+ Requires-Dist: python-dotenv>=1.0.0
14
+ Requires-Dist: python-multipart>=0.0.6
13
15
  Requires-Dist: uvicorn>=0.30.0
14
16
  Requires-Dist: watchfiles>=1.0.0
15
- Provides-Extra: state
16
- Requires-Dist: agentfs-sdk==0.4.0; extra == 'state'
17
17
  Description-Content-Type: text/markdown
18
18
 
19
19
  <h3 align="center">
@@ -15,13 +15,12 @@ class App(Function):
15
15
 
16
16
  def __init__(self, func, name, theme="default", pip=None, apt=None, copy=None, copy_public=None,
17
17
  auth=False, org=None, header=None, intro=None, title=None, plan="free", analytics=False,
18
- state=False, memory="1Gi"):
18
+ memory="1Gi"):
19
19
  if theme not in THEMES:
20
20
  raise ValueError(f"Unknown theme: {theme}. Available: {THEMES}")
21
21
  self.user_func = func
22
22
  self.theme = theme
23
23
  self.copy_public = copy_public or []
24
- self.state = state
25
24
  self.memory = memory
26
25
 
27
26
  self.config = Config(
@@ -32,7 +31,6 @@ class App(Function):
32
31
  plan=plan,
33
32
  analytics=analytics,
34
33
  org=org,
35
- state=state,
36
34
  )
37
35
 
38
36
  # Build files dict for Function (theme is inside cycls/)
@@ -43,7 +41,7 @@ class App(Function):
43
41
  super().__init__(
44
42
  func=func,
45
43
  name=name,
46
- pip=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", "python-dotenv", "docker", "agentfs-sdk", "pyturso==0.4.0rc17", *(pip or [])],
44
+ pip=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", "python-dotenv", "docker", *(pip or [])],
47
45
  apt=apt,
48
46
  copy=files,
49
47
  base_url=_get_base_url(),
@@ -9,6 +9,9 @@ import sys
9
9
  import shutil
10
10
  from pathlib import Path
11
11
  import tarfile
12
+ from dotenv import load_dotenv
13
+
14
+ load_dotenv()
12
15
 
13
16
  os.environ["DOCKER_BUILDKIT"] = "1"
14
17
 
@@ -1,7 +1,7 @@
1
1
  import json, inspect
2
2
  from pathlib import Path
3
3
  from pydantic import BaseModel
4
- from typing import Optional, Union, Any
4
+ from typing import Optional, Any
5
5
  from .auth import PK_LIVE, PK_TEST, JWKS_PROD, JWKS_TEST
6
6
 
7
7
  class Config(BaseModel):
@@ -16,7 +16,6 @@ class Config(BaseModel):
16
16
  org: Optional[str] = None
17
17
  pk: Optional[str] = None
18
18
  jwks: Optional[str] = None
19
- state: Union[bool, str] = False
20
19
 
21
20
  def set_prod(self, prod: bool):
22
21
  self.prod = prod
@@ -66,8 +65,8 @@ class Messages(list):
66
65
  return self._raw
67
66
 
68
67
  def web(func, config):
69
- from fastapi import FastAPI, Request, HTTPException, status, Depends
70
- from fastapi.responses import StreamingResponse
68
+ from fastapi import FastAPI, Request, HTTPException, status, Depends, UploadFile, File
69
+ from fastapi.responses import StreamingResponse, FileResponse
71
70
  from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
72
71
  import jwt
73
72
  from jwt import PyJWKClient
@@ -90,7 +89,6 @@ def web(func, config):
90
89
  class Context(BaseModel):
91
90
  messages: Any
92
91
  user: Optional[User] = None
93
- state: Optional[Any] = None
94
92
 
95
93
  model_config = {"arbitrary_types_allowed": True}
96
94
 
@@ -100,14 +98,6 @@ def web(func, config):
100
98
  return self.messages[-1].get("content", "")
101
99
  return ""
102
100
 
103
- @property
104
- def kv(self):
105
- return self.state.kv if self.state else None
106
-
107
- @property
108
- def fs(self):
109
- return self.state.fs if self.state else None
110
-
111
101
  app = FastAPI()
112
102
  bearer_scheme = HTTPBearer()
113
103
 
@@ -134,14 +124,7 @@ def web(func, config):
134
124
  user_data = jwt.get("user") if jwt else None
135
125
  user = User(**user_data) if user_data else None
136
126
 
137
- # Initialize state scoped to user
138
- state_instance = None
139
- if config.state:
140
- from .state import create_state
141
- user_id = user.id if user else "anonymous"
142
- state_instance = await create_state(user_id)
143
-
144
- context = Context(messages=Messages(messages), user=user, state=state_instance)
127
+ context = Context(messages=Messages(messages), user=user)
145
128
  stream = await func(context) if inspect.iscoroutinefunction(func) else func(context)
146
129
 
147
130
  if request.url.path == "/chat/completions":
@@ -154,6 +137,28 @@ def web(func, config):
154
137
  async def get_config():
155
138
  return config
156
139
 
140
+ @app.post("/attachments")
141
+ async def upload_attachment(file: UploadFile = File(...), jwt: dict = Depends(validate)):
142
+ user_id = jwt["user"]["id"]
143
+ user_dir = Path(f"/workspace/{user_id}/attachments")
144
+ user_dir.mkdir(parents=True, exist_ok=True)
145
+
146
+ file_path = user_dir / file.filename
147
+ with open(file_path, "wb") as f:
148
+ f.write(await file.read())
149
+
150
+ return {"url": f"/attachments/{file.filename}"}
151
+
152
+ @app.get("/attachments/{filename}")
153
+ async def get_attachment(filename: str, jwt: dict = Depends(validate)):
154
+ user_id = jwt["user"]["id"]
155
+ file_path = Path(f"/workspace/{user_id}/attachments") / filename
156
+
157
+ if not file_path.exists():
158
+ raise HTTPException(status_code=404, detail="File not found")
159
+
160
+ return FileResponse(file_path)
161
+
157
162
  if Path("public").is_dir():
158
163
  app.mount("/public", StaticFiles(directory="public", html=True))
159
164
  app.mount("/", StaticFiles(directory=config.public_path, html=True))
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cycls"
3
- version = "0.0.2.102"
3
+ version = "0.0.2.104"
4
4
  description = "Distribute Intelligence"
5
5
  authors = [{ name = "Mohammed J. AlRujayi", email = "mj@cycls.com" }]
6
6
  readme = "README.md"
@@ -14,11 +14,10 @@ dependencies = [
14
14
  "uvicorn>=0.30.0",
15
15
  "email-validator>=2.0.0",
16
16
  "watchfiles>=1.0.0",
17
+ "python-dotenv>=1.0.0",
18
+ "python-multipart>=0.0.6",
17
19
  ]
18
20
 
19
- [project.optional-dependencies]
20
- state = ["agentfs-sdk==0.4.0"]
21
-
22
21
  [project.scripts]
23
22
  cycls = "cycls.cli:main"
24
23
 
@@ -26,6 +25,7 @@ cycls = "cycls.cli:main"
26
25
  test = [
27
26
  "pytest>=8.0.0",
28
27
  "requests>=2.31.0",
28
+ "python-multipart>=0.0.6",
29
29
  ]
30
30
 
31
31
  [build-system]
@@ -1,6 +0,0 @@
1
- from agentfs_sdk import AgentFS, AgentFSOptions
2
-
3
-
4
- async def create_state(user_id: str = "default"):
5
- """Create state instance scoped to end-user."""
6
- return await AgentFS.open(AgentFSOptions(id=user_id))
File without changes
File without changes
File without changes
File without changes
File without changes