cycls 0.0.2.89__py3-none-any.whl → 0.0.2.91__py3-none-any.whl

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.
@@ -4,6 +4,10 @@
4
4
  <meta charset="UTF-8" />
5
5
  <title>AI Agent</title>
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <!-- Default SEO tags - dynamically updated by SEOHead component -->
8
+ <meta name="robots" content="noindex, nofollow" />
9
+ <meta name="googlebot" content="noindex, nofollow" />
10
+ <meta name="description" content="AI-powered chat interface" />
7
11
  <script src="https://cdn.tailwindcss.com?plugins=forms,typography"></script>
8
12
  <link
9
13
  rel="stylesheet"
@@ -13,8 +17,8 @@
13
17
  rel="stylesheet"
14
18
  href="https://esm.sh/katex@0.16.8/dist/katex.min.css"
15
19
  />
16
- <script type="module" crossorigin src="/assets/index-C2r4Daz3.js"></script>
17
- <link rel="stylesheet" crossorigin href="/assets/index-DWGS8zpa.css">
20
+ <script type="module" crossorigin src="/assets/index-BbLcHBku.js"></script>
21
+ <link rel="stylesheet" crossorigin href="/assets/index-oGkkm3Z8.css">
18
22
  </head>
19
23
  <body style="overflow-x: hidden">
20
24
  <div id="root"></div>
cycls/web.py CHANGED
@@ -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
4
+ from typing import Optional, Union, Any
5
5
  from .auth import PK_LIVE, PK_TEST, JWKS_PROD, JWKS_TEST
6
6
 
7
7
  class Config(BaseModel):
@@ -16,7 +16,7 @@ class Config(BaseModel):
16
16
  org: Optional[str] = None
17
17
  pk: Optional[str] = None
18
18
  jwks: Optional[str] = None
19
- debug: bool = False
19
+ state: Union[bool, str] = False
20
20
 
21
21
  def set_prod(self, prod: bool):
22
22
  self.prod = prod
@@ -37,18 +37,13 @@ def sse(item):
37
37
  if not isinstance(item, dict): item = {"type": "text", "text": item}
38
38
  return f"data: {json.dumps(item)}\n\n"
39
39
 
40
- async def encoder(stream, debug=False):
41
- import traceback
42
- try:
43
- if inspect.isasyncgen(stream):
44
- async for item in stream:
45
- if msg := sse(item): yield msg
46
- else:
47
- for item in stream:
48
- if msg := sse(item): yield msg
49
- except Exception as e:
50
- error_msg = traceback.format_exc() if debug else str(e)
51
- yield sse({"type": "thinking", "thinking": f"⚠️ Error\n```\n{error_msg}```"})
40
+ async def encoder(stream):
41
+ if inspect.isasyncgen(stream):
42
+ async for item in stream:
43
+ if msg := sse(item): yield msg
44
+ else:
45
+ for item in stream:
46
+ if msg := sse(item): yield msg
52
47
  yield "data: [DONE]\n\n"
53
48
 
54
49
  class Messages(list):
@@ -95,6 +90,9 @@ def web(func, config):
95
90
  class Context(BaseModel):
96
91
  messages: Any
97
92
  user: Optional[User] = None
93
+ state: Optional[Any] = None
94
+
95
+ model_config = {"arbitrary_types_allowed": True}
98
96
 
99
97
  @property
100
98
  def last_message(self) -> str:
@@ -102,6 +100,14 @@ def web(func, config):
102
100
  return self.messages[-1].get("content", "")
103
101
  return ""
104
102
 
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
+
105
111
  app = FastAPI()
106
112
  bearer_scheme = HTTPBearer()
107
113
 
@@ -123,23 +129,25 @@ def web(func, config):
123
129
  @app.post("/chat/cycls")
124
130
  @app.post("/chat/completions")
125
131
  async def back(request: Request, jwt: Optional[dict] = Depends(validate) if config.auth else None):
126
- import traceback
127
132
  data = await request.json()
128
133
  messages = data.get("messages")
129
134
  user_data = jwt.get("user") if jwt else None
130
- context = Context(messages = Messages(messages), user = User(**user_data) if user_data else None)
131
- try:
132
- stream = await func(context) if inspect.iscoroutinefunction(func) else func(context)
133
- except Exception as e:
134
- error_msg = traceback.format_exc() if config.debug else str(e)
135
- async def error_stream():
136
- yield sse({"type": "thinking", "thinking": f"⚠️ Error\n```\n{error_msg}```"})
137
- yield "data: [DONE]\n\n"
138
- return StreamingResponse(error_stream(), media_type="text/event-stream")
135
+ user = User(**user_data) if user_data else None
136
+
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)
145
+ stream = await func(context) if inspect.iscoroutinefunction(func) else func(context)
146
+
139
147
  if request.url.path == "/chat/completions":
140
148
  stream = openai_encoder(stream)
141
149
  elif request.url.path == "/chat/cycls":
142
- stream = encoder(stream, debug=config.debug)
150
+ stream = encoder(stream)
143
151
  return StreamingResponse(stream, media_type="text/event-stream")
144
152
 
145
153
  @app.get("/config")
@@ -1,9 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycls
3
- Version: 0.0.2.89
3
+ Version: 0.0.2.91
4
4
  Summary: Distribute Intelligence
5
5
  Author-email: "Mohammed J. AlRujayi" <mj@cycls.com>
6
- Requires-Python: >=3.9
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: agentfs-sdk==0.4.0
7
8
  Requires-Dist: cloudpickle>=3.1.1
8
9
  Requires-Dist: docker>=7.1.0
9
10
  Requires-Dist: email-validator>=2.0.0
@@ -11,6 +12,7 @@ Requires-Dist: fastapi>=0.111.0
11
12
  Requires-Dist: httpx>=0.27.0
12
13
  Requires-Dist: pyjwt>=2.8.0
13
14
  Requires-Dist: uvicorn>=0.30.0
15
+ Requires-Dist: watchfiles>=1.0.0
14
16
  Description-Content-Type: text/markdown
15
17
 
16
18
  <h3 align="center">
@@ -19,7 +21,8 @@ Distribute Intelligence
19
21
 
20
22
  <h4 align="center">
21
23
  <a href="https://cycls.com">Website</a> |
22
- <a href="https://docs.cycls.com">Docs</a>
24
+ <a href="https://docs.cycls.com">Docs</a> |
25
+ <a href="docs/tutorial.md">Tutorial</a>
23
26
  </h4>
24
27
 
25
28
  <h4 align="center">
@@ -79,7 +82,7 @@ app.deploy() # Live at https://agent.cycls.ai
79
82
  pip install cycls
80
83
  ```
81
84
 
82
- Requires Docker.
85
+ Requires Docker. See the [full tutorial](docs/tutorial.md) for a comprehensive guide.
83
86
 
84
87
  ## What You Get
85
88
 
@@ -102,6 +105,8 @@ Get an API key at [cycls.com](https://cycls.com).
102
105
 
103
106
  ## Authentication & Analytics
104
107
 
108
+ See the [tutorial](docs/tutorial.md#authentication) for full auth and monetization examples.
109
+
105
110
  ```python
106
111
  @cycls.app(pip=["openai"], auth=True, analytics=True)
107
112
  async def app(context):
@@ -118,7 +123,7 @@ async def app(context):
118
123
 
119
124
  ## Native UI Components
120
125
 
121
- Yield structured objects for rich streaming responses:
126
+ Yield structured objects for rich streaming responses. See the [tutorial](docs/tutorial.md#native-ui-components) for all component types and examples.
122
127
 
123
128
  ```python
124
129
  @cycls.app()
@@ -193,7 +198,7 @@ See [docs/streaming-protocol.md](docs/streaming-protocol.md) for frontend integr
193
198
 
194
199
  ## Declarative Infrastructure
195
200
 
196
- Define your entire runtime in the decorator:
201
+ Define your entire runtime in the decorator. See the [tutorial](docs/tutorial.md#declarative-infrastructure) for more details.
197
202
 
198
203
  ```python
199
204
  @cycls.app(
@@ -264,6 +269,13 @@ Access them at `https://your-app.cycls.ai/public/logo.png`.
264
269
 
265
270
  No YAML. No Dockerfiles. No infrastructure repo. The code is the deployment.
266
271
 
272
+ ## Learn More
273
+
274
+ - [Tutorial](docs/tutorial.md) - Comprehensive guide from basics to advanced
275
+ - [Streaming Protocol](docs/streaming-protocol.md) - Frontend integration
276
+ - [Runtime](docs/runtime.md) - Containerization details
277
+ - [Examples](examples/) - Working code samples
278
+
267
279
  ## License
268
280
 
269
281
  MIT
@@ -0,0 +1,15 @@
1
+ cycls/__init__.py,sha256=efbq0vRijGOByKtz9bRF8WQFYmnPSgZV1DH-54s6iwQ,493
2
+ cycls/app.py,sha256=_-RoN8JuNE7fWSOHk80GpQ2GyaoXv-vtUWc_W8tDzm0,3243
3
+ cycls/auth.py,sha256=xkndHZyCfnlertMMEKerCJjf23N3fVcTRVTTSXTTuzg,247
4
+ cycls/cli.py,sha256=cVbIkTDnVofohvByyYUrXF_RYDQZVQECJqo7cPBPJfs,4781
5
+ cycls/function.py,sha256=FP94mCd5hfCASFwsTSYny7KOEsJOh3LME9oZgiVfgzo,14795
6
+ cycls/state.py,sha256=3RL_BqcbfiAkJ6byeEo4Q9L99VurYyYKYJJhJhEVmAo,210
7
+ cycls/web.py,sha256=v-hyWL9nlh4SyqAVgRzOkx96YuBntxqw6Q6phpAMDv0,6172
8
+ cycls/themes/default/index.html,sha256=_qizHg7Yb8jRvroVP_QJ4M8du1mUwI5VM04oIZjffWI,1082
9
+ cycls/themes/default/assets/index-BbLcHBku.js,sha256=ajrbPsIF5jxg-TrgpEkPoUL4WvyDvNB2Ss9zbJvRVOg,1357000
10
+ cycls/themes/default/assets/index-oGkkm3Z8.css,sha256=xHhbrJvvz8mIN_Otrl6nawKJcA41L3tCFhVEOstGhVU,6498
11
+ cycls/themes/dev/index.html,sha256=QJBHkdNuMMiwQU7o8dN8__8YQeQB45D37D-NCXIWB2Q,11585
12
+ cycls-0.0.2.91.dist-info/METADATA,sha256=I_CrgFp9ApDj-_UKD9RbX1wb5lYVTyaPVhTLQdFRooE,8829
13
+ cycls-0.0.2.91.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ cycls-0.0.2.91.dist-info/entry_points.txt,sha256=CktT5eNvW_Qxomf7L_Ez_GdUbL6qAfx_Utm6_HtUJwE,41
15
+ cycls-0.0.2.91.dist-info/RECORD,,