cycls 0.0.2.72__py3-none-any.whl → 0.0.2.74__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.
cycls/__init__.py CHANGED
@@ -1,2 +1,20 @@
1
- from .sdk import Agent, function
2
- from .runtime import Runtime
1
+ import sys
2
+ from types import ModuleType
3
+ from .sdk import function, agent
4
+ from .runtime import Runtime
5
+
6
+ class _Module(ModuleType):
7
+ def __getattr__(self, name):
8
+ from . import sdk
9
+ if name in ("api_key", "base_url"):
10
+ return getattr(sdk, name)
11
+ raise AttributeError(f"module 'cycls' has no attribute '{name}'")
12
+
13
+ def __setattr__(self, name, value):
14
+ from . import sdk
15
+ if name in ("api_key", "base_url"):
16
+ setattr(sdk, name, value)
17
+ return
18
+ super().__setattr__(name, value)
19
+
20
+ sys.modules[__name__].__class__ = _Module
cycls/sdk.py CHANGED
@@ -1,30 +1,21 @@
1
- import os, time, inspect, uvicorn
1
+ import os, time, uvicorn
2
2
  from .runtime import Runtime
3
3
  from .web import web, Config
4
4
  from .auth import PK_LIVE, PK_TEST, JWKS_PROD, JWKS_TEST
5
5
  import importlib.resources
6
- from pydantic import BaseModel
7
- from typing import Callable
8
6
 
9
7
  CYCLS_PATH = importlib.resources.files('cycls')
10
8
 
11
- class RegisteredAgent(BaseModel):
12
- func: Callable
13
- name: str
14
- domain: str
15
- config: Config
16
-
17
- def set_prod(config: Config, prod: bool):
18
- config.prod = prod
19
- config.pk = PK_LIVE if prod else PK_TEST
20
- config.jwks = JWKS_PROD if prod else JWKS_TEST
9
+ # Module-level configuration
10
+ api_key = None
11
+ base_url = None
21
12
 
22
13
  themes = {
23
14
  "default": CYCLS_PATH.joinpath('default-theme'),
24
15
  "dev": CYCLS_PATH.joinpath('dev-theme'),
25
16
  }
26
17
 
27
- def resolve_theme(theme):
18
+ def _resolve_theme(theme):
28
19
  """Resolve theme - accepts string name or path"""
29
20
  if isinstance(theme, str):
30
21
  if theme in themes:
@@ -32,77 +23,54 @@ def resolve_theme(theme):
32
23
  raise ValueError(f"Unknown theme: {theme}. Available: {list(themes.keys())}")
33
24
  return theme
34
25
 
35
- def function(python_version=None, pip=None, apt=None, run_commands=None, copy=None, name=None, base_url=None, key=None):
36
- # """
37
- # A decorator factory that transforms a Python function into a containerized,
38
- # remotely executable object.
39
- def decorator(func):
40
- Name = name or func.__name__
41
- copy_dict = {i:i for i in copy or []}
42
- return Runtime(func, Name.replace('_', '-'), python_version, pip, apt, run_commands, copy_dict, base_url, key)
43
- return decorator
26
+ def _set_prod(config: Config, prod: bool):
27
+ config.prod = prod
28
+ config.pk = PK_LIVE if prod else PK_TEST
29
+ config.jwks = JWKS_PROD if prod else JWKS_TEST
30
+
31
+ class AgentRuntime:
32
+ """Wraps an agent function with local/deploy/modal capabilities."""
33
+
34
+ def __init__(self, func, name, theme, pip, apt, copy, copy_public, modal_keys, auth, org, domain, header, intro, title, plan, analytics):
35
+ self.func = func
36
+ self.name = name
37
+ self.theme = _resolve_theme(theme)
38
+ self.pip = pip
39
+ self.apt = apt
40
+ self.copy = copy
41
+ self.copy_public = copy_public
42
+ self.modal_keys = modal_keys
43
+ self.domain = domain or f"{name}.cycls.ai"
44
+
45
+ self.config = Config(
46
+ header=header,
47
+ intro=intro,
48
+ title=title,
49
+ auth=auth,
50
+ plan=plan,
51
+ analytics=analytics,
52
+ org=org,
53
+ )
44
54
 
45
- class Agent:
46
- def __init__(self, theme="default", org=None, api_token=None, pip=[], apt=[], copy=[], copy_public=[], modal_keys=["",""], key=None, base_url=None):
47
- self.org, self.api_token = org, api_token
48
- self.theme = resolve_theme(theme)
49
- self.key, self.modal_keys, self.pip, self.apt, self.copy, self.copy_public = key, modal_keys, pip, apt, copy, copy_public
50
- self.base_url = base_url
51
-
52
- self.registered_functions = []
53
-
54
- def __call__(self, name=None, header="", intro="", title="", domain=None, auth=False, tier="free", analytics=False):
55
- if tier=="cycls_pass":
56
- auth=True
57
- analytics=True
58
- def decorator(f):
59
- agent_name = name or f.__name__.replace('_', '-')
60
- self.registered_functions.append(RegisteredAgent(
61
- func=f,
62
- name=agent_name,
63
- domain=domain or f"{agent_name}.cycls.ai",
64
- config=Config(
65
- header=header,
66
- intro=intro,
67
- title=title,
68
- auth=auth,
69
- tier=tier,
70
- analytics=analytics,
71
- org=self.org,
72
- ),
73
- ))
74
- return f
75
- return decorator
55
+ def __call__(self, *args, **kwargs):
56
+ """Make the runtime callable - delegates to the wrapped function."""
57
+ return self.func(*args, **kwargs)
76
58
 
77
59
  def _local(self, port=8080, watch=True):
78
60
  """Run directly with uvicorn (no Docker)."""
79
- if not self.registered_functions:
80
- print("Error: No @agent decorated function found.")
81
- return
82
-
83
- agent = self.registered_functions[0]
84
- if len(self.registered_functions) > 1:
85
- print(f"⚠️ Warning: Multiple agents found. Running '{agent.name}'.")
86
- print(f"🚀 Starting local server at localhost:{port}")
87
- agent.config.public_path = self.theme
88
- set_prod(agent.config, False)
89
- uvicorn.run(web(agent.func, agent.config), host="0.0.0.0", port=port, reload=watch)
90
- return
61
+ print(f"Starting local server at localhost:{port}")
62
+ self.config.public_path = self.theme
63
+ _set_prod(self.config, False)
64
+ uvicorn.run(web(self.func, self.config), host="0.0.0.0", port=port, reload=watch)
91
65
 
92
66
  def _runtime(self, prod=False):
93
- """Create a Runtime instance for the first registered agent."""
94
- if not self.registered_functions:
95
- print("Error: No @agent decorated function found.")
96
- return None
97
-
98
- agent = self.registered_functions[0]
99
- if len(self.registered_functions) > 1:
100
- print(f"⚠️ Warning: Multiple agents found. Running '{agent.name}'.")
67
+ """Create a Runtime instance for deployment."""
68
+ _set_prod(self.config, prod)
69
+ config_dict = self.config.model_dump()
101
70
 
102
- set_prod(agent.config, prod)
103
- func = agent.func
104
- name = agent.name
105
- config_dict = agent.config.model_dump()
71
+ # Extract to local variables to avoid capturing self in lambda (cloudpickle issue)
72
+ func = self.func
73
+ name = self.name
106
74
 
107
75
  files = {str(self.theme): "theme", str(CYCLS_PATH)+"/web.py": "web.py"}
108
76
  files.update({f: f for f in self.copy})
@@ -114,73 +82,100 @@ class Agent:
114
82
  apt_packages=self.apt,
115
83
  pip_packages=["fastapi[standard]", "pyjwt", "cryptography", "uvicorn", *self.pip],
116
84
  copy=files,
117
- base_url=self.base_url,
118
- api_key=self.key
85
+ base_url=base_url,
86
+ api_key=api_key
119
87
  )
120
88
 
121
89
  def local(self, port=8080, watch=True):
122
90
  """Run locally in Docker with file watching by default."""
123
- # Child process spawned by watcher - run without watch
124
91
  if os.environ.get('_CYCLS_WATCH_CHILD'):
125
92
  watch = False
126
93
  runtime = self._runtime(prod=False)
127
- if runtime:
128
- runtime.watch(port=port) if watch else runtime.run(port=port)
94
+ runtime.watch(port=port) if watch else runtime.run(port=port)
129
95
 
130
96
  def deploy(self, port=8080):
131
97
  """Deploy to production."""
132
- if self.key is None:
133
- print("🛑 Error: Please add your Cycls API key")
98
+ if api_key is None:
99
+ print("Error: Please set cycls.api_key")
134
100
  return
135
101
  runtime = self._runtime(prod=True)
136
- if runtime:
137
- runtime.deploy(port=port)
138
-
102
+ runtime.deploy(port=port)
103
+
139
104
  def modal(self, prod=False):
140
105
  import modal
141
106
  from modal.runner import run_app
142
- self.client = modal.Client.from_credentials(*self.modal_keys)
107
+
108
+ # Extract to local variables to avoid capturing self in lambda
109
+ func = self.func
110
+ name = self.name
111
+ domain = self.domain
112
+
113
+ client = modal.Client.from_credentials(*self.modal_keys)
143
114
  image = (modal.Image.debian_slim()
144
115
  .pip_install("fastapi[standard]", "pyjwt", "cryptography", *self.pip)
145
116
  .apt_install(*self.apt)
146
117
  .add_local_dir(self.theme, "/root/theme")
147
118
  .add_local_file(str(CYCLS_PATH)+"/web.py", "/root/web.py"))
148
-
119
+
149
120
  for item in self.copy:
150
121
  image = image.add_local_file(item, f"/root/{item}") if "." in item else image.add_local_dir(item, f'/root/{item}')
151
-
122
+
152
123
  for item in self.copy_public:
153
124
  image = image.add_local_file(item, f"/root/public/{item}") if "." in item else image.add_local_dir(item, f'/root/public/{item}')
154
125
 
155
- self.app = modal.App("development", image=image)
156
-
157
- if not self.registered_functions:
158
- print("Error: No @agent decorated function found.")
159
- return
126
+ app = modal.App("development", image=image)
127
+
128
+ _set_prod(self.config, prod)
129
+ config_dict = self.config.model_dump()
130
+
131
+ app.function(serialized=True, name=name)(
132
+ modal.asgi_app(label=name, custom_domains=[domain])
133
+ (lambda: __import__("web").web(func, config_dict))
134
+ )
160
135
 
161
- for agent in self.registered_functions:
162
- set_prod(agent.config, prod)
163
- func = agent.func
164
- name = agent.name
165
- domain = agent.domain
166
- config_dict = agent.config.model_dump()
167
- self.app.function(serialized=True, name=name)(
168
- modal.asgi_app(label=name, custom_domains=[domain])
169
- (lambda: __import__("web").web(func, config_dict))
170
- )
171
136
  if prod:
172
- for agent in self.registered_functions:
173
- print(f"✅ Deployed to ⇒ https://{agent.domain}")
174
- self.app.deploy(client=self.client, name=self.registered_functions[0].name)
175
- return
137
+ print(f"Deployed to => https://{domain}")
138
+ app.deploy(client=client, name=name)
176
139
  else:
177
140
  with modal.enable_output():
178
- run_app(app=self.app, client=self.client)
179
- print(" Modal development server is running. Press Ctrl+C to stop.")
180
- with modal.enable_output(), run_app(app=self.app, client=self.client):
141
+ run_app(app=app, client=client)
142
+ print("Modal development server is running. Press Ctrl+C to stop.")
143
+ with modal.enable_output(), run_app(app=app, client=client):
181
144
  while True: time.sleep(10)
182
145
 
183
- # docker system prune -af
184
- # poetry config pypi-token.pypi <your-token>
185
- # poetry run python cake.py
186
- # poetry publish --build
146
+
147
+ def agent(name=None, pip=[], apt=[], copy=[], copy_public=[], theme="default", modal_keys=["", ""], auth=False, org=None, domain=None, header="", intro="", title="", plan="free", analytics=False):
148
+ """Decorator that transforms a function into a deployable agent."""
149
+ if plan == "cycls_pass":
150
+ auth = True
151
+ analytics = True
152
+
153
+ def decorator(func):
154
+ agent_name = name or func.__name__.replace('_', '-')
155
+ return AgentRuntime(
156
+ func=func,
157
+ name=agent_name,
158
+ theme=theme,
159
+ pip=pip,
160
+ apt=apt,
161
+ copy=copy,
162
+ copy_public=copy_public,
163
+ modal_keys=modal_keys,
164
+ auth=auth,
165
+ org=org,
166
+ domain=domain,
167
+ header=header,
168
+ intro=intro,
169
+ title=title,
170
+ plan=plan,
171
+ analytics=analytics,
172
+ )
173
+ return decorator
174
+
175
+ def function(python_version=None, pip=None, apt=None, run_commands=None, copy=None, name=None):
176
+ """Decorator that transforms a Python function into a containerized, remotely executable object."""
177
+ def decorator(func):
178
+ func_name = name or func.__name__
179
+ copy_dict = {i: i for i in copy or []}
180
+ return Runtime(func, func_name.replace('_', '-'), python_version, pip, apt, run_commands, copy_dict, base_url, api_key)
181
+ return decorator
cycls/web.py CHANGED
@@ -10,7 +10,7 @@ class Config(BaseModel):
10
10
  title: str = ""
11
11
  prod: bool = False
12
12
  auth: bool = False
13
- tier: str = "free"
13
+ plan: str = "free"
14
14
  analytics: bool = False
15
15
  org: Optional[str] = None
16
16
  pk: str = ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cycls
3
- Version: 0.0.2.72
3
+ Version: 0.0.2.74
4
4
  Summary: Distribute Intelligence
5
5
  Author: Mohammed J. AlRujayi
6
6
  Author-email: mj@cycls.com
@@ -32,6 +32,7 @@ Distribute Intelligence
32
32
 
33
33
  <h4 align="center">
34
34
  <a href="https://pypi.python.org/pypi/cycls"><img src="https://img.shields.io/pypi/v/cycls.svg?label=cycls+pypi&color=blueviolet" alt="cycls Python package on PyPi" /></a>
35
+ <a href="https://github.com/Cycls/cycls/actions/workflows/tests.yml"><img src="https://github.com/Cycls/cycls/actions/workflows/tests.yml/badge.svg" alt="Tests" /></a>
35
36
  <a href="https://blog.cycls.com"><img src="https://img.shields.io/badge/newsletter-blueviolet.svg?logo=substack&label=cycls" alt="Cycls newsletter" /></a>
36
37
  <a href="https://x.com/cyclsai">
37
38
  <img src="https://img.shields.io/twitter/follow/CyclsAI" alt="Cycls Twitter" />
@@ -46,31 +47,32 @@ The open-source SDK for distributing AI agents.
46
47
 
47
48
  ## Distribute Intelligence
48
49
 
49
- AI capabilities shouldn't be locked in notebooks or trapped behind months of infrastructure work. Cycls turns your Python functions into production services - complete with APIs, interfaces, auth, and analytics. You focus on the intelligence. Cycls handles the distribution.
50
-
51
50
  Write a function. Deploy it as an API, a web interface, or both. Add authentication, analytics, and monetization with flags.
52
51
 
53
52
  ```python
54
53
  import cycls
55
54
 
56
- agent = cycls.Agent(pip=["openai"])
55
+ cycls.api_key = "YOUR_CYCLS_API_KEY"
57
56
 
58
- @agent("my-agent", auth=True, analytics=True)
59
- async def chat(context):
57
+ @cycls.agent(pip=["openai"])
58
+ async def agent(context):
60
59
  from openai import AsyncOpenAI
61
60
  client = AsyncOpenAI()
62
61
 
63
- response = await client.chat.completions.create(
64
- model="gpt-4o",
65
- messages=context.messages,
66
- stream=True
62
+ stream = await client.responses.create(
63
+ model="o3-mini",
64
+ input=context.messages,
65
+ stream=True,
66
+ reasoning={"effort": "medium", "summary": "auto"},
67
67
  )
68
68
 
69
- async for chunk in response:
70
- if chunk.choices[0].delta.content:
71
- yield chunk.choices[0].delta.content
69
+ async for event in stream:
70
+ if event.type == "response.reasoning_summary_text.delta":
71
+ yield {"type": "thinking", "thinking": event.delta} # Renders as thinking bubble
72
+ elif event.type == "response.output_text.delta":
73
+ yield event.delta
72
74
 
73
- agent.deploy() # Live at https://my-agent.cycls.ai
75
+ agent.deploy() # Live at https://agent.cycls.ai
74
76
  ```
75
77
 
76
78
  ## Installation
@@ -87,7 +89,7 @@ Requires Docker.
87
89
  - **Web Interface** - Chat UI served automatically
88
90
  - **Authentication** - `auth=True` enables JWT-based access control
89
91
  - **Analytics** - `analytics=True` tracks usage
90
- - **Monetization** - `tier="cycls_pass"` integrates with [Cycls Pass](https://cycls.ai) subscriptions
92
+ - **Monetization** - `plan="cycls_pass"` integrates with [Cycls Pass](https://cycls.ai) subscriptions
91
93
  - **Native UI Components** - Render thinking bubbles, tables, code blocks in responses
92
94
 
93
95
  ## Running
@@ -95,17 +97,33 @@ Requires Docker.
95
97
  ```python
96
98
  agent.local() # Development with hot-reload (localhost:8080)
97
99
  agent.local(watch=False) # Development without hot-reload
98
- agent.deploy() # Production: https://agent-name.cycls.ai
100
+ agent.deploy() # Production: https://agent.cycls.ai
99
101
  ```
100
102
 
101
103
  Get an API key at [cycls.com](https://cycls.com).
102
104
 
105
+ ## Authentication & Analytics
106
+
107
+ ```python
108
+ @cycls.agent(pip=["openai"], auth=True, analytics=True)
109
+ async def agent(context):
110
+ # context.user available when auth=True
111
+ user = context.user # User(id, email, name, plans)
112
+ yield f"Hello {user.name}!"
113
+ ```
114
+
115
+ | Flag | Description |
116
+ |------|-------------|
117
+ | `auth=True` | Universal user pool via Cycls Pass (Clerk-based). You can also use your own Clerk auth. |
118
+ | `analytics=True` | Rich usage metrics available on the Cycls dashboard. |
119
+ | `plan="cycls_pass"` | Monetization via Cycls Pass subscriptions. Enables both auth and analytics. |
120
+
103
121
  ## Native UI Components
104
122
 
105
123
  Yield structured objects for rich streaming responses:
106
124
 
107
125
  ```python
108
- @agent()
126
+ @cycls.agent()
109
127
  async def demo(context):
110
128
  yield {"type": "thinking", "thinking": "Analyzing the request..."}
111
129
  yield "Here's what I found:\n\n"
@@ -128,32 +146,26 @@ async def demo(context):
128
146
  | `{"type": "callout", "callout": "...", "style": "..."}` | Yes |
129
147
  | `{"type": "image", "src": "..."}` | Yes |
130
148
 
131
- ### Reasoning Models
149
+ ### Thinking Bubbles
132
150
 
133
- ```python
134
- @agent()
135
- async def chat(context):
136
- from openai import AsyncOpenAI
137
- client = AsyncOpenAI()
151
+ The `{"type": "thinking", "thinking": "..."}` component renders as a collapsible thinking bubble in the UI. Each yield appends to the same bubble until a different component type is yielded:
138
152
 
139
- stream = await client.responses.create(
140
- model="o3-mini",
141
- input=context.messages,
142
- stream=True,
143
- reasoning={"effort": "medium", "summary": "auto"},
144
- )
153
+ ```python
154
+ # Multiple yields build one thinking bubble
155
+ yield {"type": "thinking", "thinking": "Let me "}
156
+ yield {"type": "thinking", "thinking": "analyze this..."}
157
+ yield {"type": "thinking", "thinking": " Done thinking."}
145
158
 
146
- async for event in stream:
147
- if event.type == "response.reasoning_summary_text.delta":
148
- yield {"type": "thinking", "thinking": event.delta}
149
- elif event.type == "response.output_text.delta":
150
- yield event.delta
159
+ # Then output the response
160
+ yield "Here's what I found..."
151
161
  ```
152
162
 
163
+ This works seamlessly with OpenAI's reasoning models - just map reasoning summaries to the thinking component.
164
+
153
165
  ## Context Object
154
166
 
155
167
  ```python
156
- @agent()
168
+ @cycls.agent()
157
169
  async def chat(context):
158
170
  context.messages # [{"role": "user", "content": "..."}]
159
171
  context.messages.raw # Full data including UI component parts
@@ -183,16 +195,17 @@ See [docs/streaming-protocol.md](docs/streaming-protocol.md) for frontend integr
183
195
 
184
196
  ## Declarative Infrastructure
185
197
 
186
- Define your entire runtime in Python:
198
+ Define your entire runtime in the decorator:
187
199
 
188
200
  ```python
189
- agent = cycls.Agent(
201
+ @cycls.agent(
190
202
  pip=["openai", "pandas", "numpy"],
191
203
  apt=["ffmpeg", "libmagic1"],
192
- run_commands=["curl -sSL https://example.com/setup.sh | bash"],
193
204
  copy=["./utils.py", "./models/", "/absolute/path/to/config.json"],
194
205
  copy_public=["./assets/logo.png", "./static/"],
195
206
  )
207
+ async def my_agent(context):
208
+ ...
196
209
  ```
197
210
 
198
211
  ### `pip` - Python Packages
@@ -211,17 +224,6 @@ Install system-level dependencies via apt-get. Need ffmpeg for audio processing?
211
224
  apt=["ffmpeg", "imagemagick", "libpq-dev"]
212
225
  ```
213
226
 
214
- ### `run_commands` - Shell Commands
215
-
216
- Run arbitrary shell commands during the container build. Useful for custom setup scripts, downloading assets, or any build-time configuration.
217
-
218
- ```python
219
- run_commands=[
220
- "curl -sSL https://example.com/setup.sh | bash",
221
- "chmod +x /app/scripts/*.sh"
222
- ]
223
- ```
224
-
225
227
  ### `copy` - Bundle Files and Directories
226
228
 
227
229
  Include local files and directories in your container. Works with both relative and absolute paths. Copies files and entire directory trees.
@@ -237,7 +239,7 @@ copy=[
237
239
  Then import them in your function:
238
240
 
239
241
  ```python
240
- @agent()
242
+ @cycls.agent(copy=["./utils.py"])
241
243
  async def chat(context):
242
244
  from utils import helper_function # Your bundled module
243
245
  ...
@@ -1,4 +1,4 @@
1
- cycls/__init__.py,sha256=bVT0dYTXLdSC3ZURgtm-DEOj-VO6RUM6zGsJB0zuj6Y,61
1
+ cycls/__init__.py,sha256=vyI1d_8VP4XW7MliFuUs_P3O9KQxyCwQu-JkxrCyhPQ,597
2
2
  cycls/auth.py,sha256=xkndHZyCfnlertMMEKerCJjf23N3fVcTRVTTSXTTuzg,247
3
3
  cycls/cli.py,sha256=AKf0z7ZLau3GvBVR_IhB7agmq4nVaHkcuUafNyvv2_A,7978
4
4
  cycls/default-theme/assets/index-B0ZKcm_V.css,sha256=wK9-NhEB8xPcN9Zv69zpOcfGTlFbMwyC9WqTmSKUaKw,6546
@@ -6,9 +6,9 @@ cycls/default-theme/assets/index-D5EDcI4J.js,sha256=sN4qRcAXa7DBd9JzmVcCoCwH4l8c
6
6
  cycls/default-theme/index.html,sha256=bM-yW_g0cGrV40Q5yY3ccY0fM4zI1Wuu5I8EtGFJIxs,828
7
7
  cycls/dev-theme/index.html,sha256=QJBHkdNuMMiwQU7o8dN8__8YQeQB45D37D-NCXIWB2Q,11585
8
8
  cycls/runtime.py,sha256=lg7XKHd9fLV_bYksHv2LHf3Lq7HPAC3K5Tr8pNgQ7sM,21641
9
- cycls/sdk.py,sha256=6oRKP44TJN9HKdNw9OYzDlZFDUMUhoCMt8TEwyu26dI,7368
10
- cycls/web.py,sha256=3M3qaWTNY3dpgd7Vq5aXREp-cIFsHrDqBQ1YkGrOaUk,4659
11
- cycls-0.0.2.72.dist-info/METADATA,sha256=8co1QLXI-88tAFHSuaWeZchypHvODZVC1Qjlo9A_WDE,8008
12
- cycls-0.0.2.72.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
13
- cycls-0.0.2.72.dist-info/entry_points.txt,sha256=vEhqUxFhhuzCKWtq02LbMnT3wpUqdfgcM3Yh-jjXom8,40
14
- cycls-0.0.2.72.dist-info/RECORD,,
9
+ cycls/sdk.py,sha256=X8-VAVqtksO0VGJIxlg02HLmeFpwtwMHWu9PNksS5kw,6620
10
+ cycls/web.py,sha256=_QNH8K55vTm90Z7tvcRKal5IybjkB1GY7Pf9p3qu3r8,4659
11
+ cycls-0.0.2.74.dist-info/METADATA,sha256=IXA6mD4bkkGRnGZjK_rQYDda-fEE8nI2ucgVVVIsJgQ,8419
12
+ cycls-0.0.2.74.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
13
+ cycls-0.0.2.74.dist-info/entry_points.txt,sha256=vEhqUxFhhuzCKWtq02LbMnT3wpUqdfgcM3Yh-jjXom8,40
14
+ cycls-0.0.2.74.dist-info/RECORD,,