cycls 0.0.2.69__py3-none-any.whl → 0.0.2.70__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/runtime.py
CHANGED
|
@@ -337,6 +337,7 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
337
337
|
return
|
|
338
338
|
|
|
339
339
|
import inspect
|
|
340
|
+
import subprocess
|
|
340
341
|
|
|
341
342
|
# Get the main script (the outermost .py file in the stack)
|
|
342
343
|
main_script = None
|
|
@@ -362,75 +363,37 @@ COPY {self.payload_file} {self.io_dir}/
|
|
|
362
363
|
print()
|
|
363
364
|
|
|
364
365
|
while True:
|
|
365
|
-
# Run the
|
|
366
|
-
print(f"🚀 Running
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
ports_mapping = {f'{port}/tcp': port} if port else None
|
|
372
|
-
|
|
373
|
-
with tempfile.TemporaryDirectory() as tmpdir_str:
|
|
374
|
-
tmpdir = Path(tmpdir_str)
|
|
375
|
-
payload_path = tmpdir / self.payload_file
|
|
376
|
-
|
|
377
|
-
with payload_path.open('wb') as f:
|
|
378
|
-
cloudpickle.dump((self.func, args, kwargs), f)
|
|
366
|
+
# Run the script in a subprocess so we survive errors
|
|
367
|
+
print(f"🚀 Running {main_script.name}...")
|
|
368
|
+
proc = subprocess.Popen(
|
|
369
|
+
[sys.executable, str(main_script)],
|
|
370
|
+
env={**os.environ, '_CYCLS_WATCH_CHILD': '1'}
|
|
371
|
+
)
|
|
379
372
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
373
|
+
try:
|
|
374
|
+
# Watch for changes
|
|
375
|
+
for changes in watchfiles_watch(*watch_paths):
|
|
376
|
+
changed_files = [str(c[1]) for c in changes]
|
|
377
|
+
print(f"\n🔄 Changes detected:")
|
|
378
|
+
for f in changed_files:
|
|
379
|
+
print(f" {f}")
|
|
380
|
+
break
|
|
381
|
+
|
|
382
|
+
print("\n🔄 Restarting...\n")
|
|
383
|
+
proc.terminate()
|
|
384
|
+
try:
|
|
385
|
+
proc.wait(timeout=3)
|
|
386
|
+
except subprocess.TimeoutExpired:
|
|
387
|
+
proc.kill()
|
|
389
388
|
|
|
389
|
+
except KeyboardInterrupt:
|
|
390
|
+
print("\n🛑 Stopping...")
|
|
391
|
+
proc.terminate()
|
|
390
392
|
try:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
def watch_thread():
|
|
397
|
-
for changes in watchfiles_watch(*watch_paths):
|
|
398
|
-
changed_files = [str(c[1]) for c in changes]
|
|
399
|
-
print(f"\n🔄 Changes detected:")
|
|
400
|
-
for f in changed_files:
|
|
401
|
-
print(f" {f}")
|
|
402
|
-
change_detected.set()
|
|
403
|
-
break
|
|
404
|
-
|
|
405
|
-
watcher = threading.Thread(target=watch_thread, daemon=True)
|
|
406
|
-
watcher.start()
|
|
407
|
-
|
|
408
|
-
# Stream container logs in separate thread
|
|
409
|
-
def log_thread():
|
|
410
|
-
for chunk in container.logs(stream=True, follow=True):
|
|
411
|
-
if change_detected.is_set():
|
|
412
|
-
break
|
|
413
|
-
print(chunk.decode('utf-8').strip())
|
|
414
|
-
|
|
415
|
-
logger = threading.Thread(target=log_thread, daemon=True)
|
|
416
|
-
logger.start()
|
|
417
|
-
|
|
418
|
-
# Wait for change or interrupt
|
|
419
|
-
while not change_detected.is_set():
|
|
420
|
-
time.sleep(0.5)
|
|
421
|
-
|
|
422
|
-
print("\n🔄 Restarting...")
|
|
423
|
-
container.stop(timeout=2)
|
|
424
|
-
container.remove()
|
|
425
|
-
# Re-exec the main script
|
|
426
|
-
print(f"🔄 Re-executing {main_script.name}...\n")
|
|
427
|
-
os.execv(sys.executable, [sys.executable, str(main_script)])
|
|
428
|
-
|
|
429
|
-
except KeyboardInterrupt:
|
|
430
|
-
print("\n🛑 Stopping...")
|
|
431
|
-
container.stop(timeout=2)
|
|
432
|
-
container.remove()
|
|
433
|
-
return
|
|
393
|
+
proc.wait(timeout=3)
|
|
394
|
+
except subprocess.TimeoutExpired:
|
|
395
|
+
proc.kill()
|
|
396
|
+
return
|
|
434
397
|
|
|
435
398
|
def build(self, *args, **kwargs):
|
|
436
399
|
"""Builds a self-contained, deployable Docker image locally."""
|
cycls/sdk.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import time, inspect, uvicorn
|
|
1
|
+
import os, time, inspect, 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
|
|
@@ -74,7 +74,8 @@ class Agent:
|
|
|
74
74
|
return f
|
|
75
75
|
return decorator
|
|
76
76
|
|
|
77
|
-
def
|
|
77
|
+
def _local(self, port=8080, watch=True):
|
|
78
|
+
"""Run directly with uvicorn (no Docker)."""
|
|
78
79
|
if not self.registered_functions:
|
|
79
80
|
print("Error: No @agent decorated function found.")
|
|
80
81
|
return
|
|
@@ -85,19 +86,14 @@ class Agent:
|
|
|
85
86
|
print(f"🚀 Starting local server at localhost:{port}")
|
|
86
87
|
agent.config.public_path = self.theme
|
|
87
88
|
set_prod(agent.config, False)
|
|
88
|
-
uvicorn.run(web(agent.func, agent.config), host="0.0.0.0", port=port)
|
|
89
|
+
uvicorn.run(web(agent.func, agent.config), host="0.0.0.0", port=port, reload=watch)
|
|
89
90
|
return
|
|
90
91
|
|
|
91
|
-
def
|
|
92
|
+
def _runtime(self, prod=False):
|
|
93
|
+
"""Create a Runtime instance for the first registered agent."""
|
|
92
94
|
if not self.registered_functions:
|
|
93
95
|
print("Error: No @agent decorated function found.")
|
|
94
|
-
return
|
|
95
|
-
if (self.key is None) and prod:
|
|
96
|
-
print("🛑 Error: Please add your Cycls API key")
|
|
97
|
-
return
|
|
98
|
-
if prod and watch:
|
|
99
|
-
print("⚠️ Warning: watch=True ignored in production mode.")
|
|
100
|
-
watch = False
|
|
96
|
+
return None
|
|
101
97
|
|
|
102
98
|
agent = self.registered_functions[0]
|
|
103
99
|
if len(self.registered_functions) > 1:
|
|
@@ -112,7 +108,7 @@ class Agent:
|
|
|
112
108
|
files.update({f: f for f in self.copy})
|
|
113
109
|
files.update({f: f"public/{f}" for f in self.copy_public})
|
|
114
110
|
|
|
115
|
-
|
|
111
|
+
return Runtime(
|
|
116
112
|
func=lambda port: __import__("web").serve(func, config_dict, name, port),
|
|
117
113
|
name=name,
|
|
118
114
|
apt_packages=self.apt,
|
|
@@ -121,13 +117,24 @@ class Agent:
|
|
|
121
117
|
base_url=self.base_url,
|
|
122
118
|
api_key=self.key
|
|
123
119
|
)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
120
|
+
|
|
121
|
+
def local(self, port=8080, watch=True):
|
|
122
|
+
"""Run locally in Docker with file watching by default."""
|
|
123
|
+
# Child process spawned by watcher - run without watch
|
|
124
|
+
if os.environ.get('_CYCLS_WATCH_CHILD'):
|
|
125
|
+
watch = False
|
|
126
|
+
runtime = self._runtime(prod=False)
|
|
127
|
+
if runtime:
|
|
128
|
+
runtime.watch(port=port) if watch else runtime.run(port=port)
|
|
129
|
+
|
|
130
|
+
def deploy(self, port=8080):
|
|
131
|
+
"""Deploy to production."""
|
|
132
|
+
if self.key is None:
|
|
133
|
+
print("🛑 Error: Please add your Cycls API key")
|
|
134
|
+
return
|
|
135
|
+
runtime = self._runtime(prod=True)
|
|
136
|
+
if runtime:
|
|
137
|
+
runtime.deploy(port=port)
|
|
131
138
|
|
|
132
139
|
def modal(self, prod=False):
|
|
133
140
|
import modal
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cycls
|
|
3
|
-
Version: 0.0.2.
|
|
3
|
+
Version: 0.0.2.70
|
|
4
4
|
Summary: Distribute Intelligence
|
|
5
5
|
Author: Mohammed J. AlRujayi
|
|
6
6
|
Author-email: mj@cycls.com
|
|
@@ -70,7 +70,7 @@ async def chat(context):
|
|
|
70
70
|
if chunk.choices[0].delta.content:
|
|
71
71
|
yield chunk.choices[0].delta.content
|
|
72
72
|
|
|
73
|
-
agent.deploy(
|
|
73
|
+
agent.deploy() # Live at https://my-agent.cycls.ai
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
## Installation
|
|
@@ -90,11 +90,12 @@ Requires Docker.
|
|
|
90
90
|
- **Monetization** - `tier="cycls_pass"` integrates with [Cycls Pass](https://cycls.ai) subscriptions
|
|
91
91
|
- **Native UI Components** - Render thinking bubbles, tables, code blocks in responses
|
|
92
92
|
|
|
93
|
-
##
|
|
93
|
+
## Running
|
|
94
94
|
|
|
95
95
|
```python
|
|
96
|
-
agent.
|
|
97
|
-
agent.
|
|
96
|
+
agent.local() # Development with hot-reload (localhost:8080)
|
|
97
|
+
agent.local(watch=False) # Development without hot-reload
|
|
98
|
+
agent.deploy() # Production: https://agent-name.cycls.ai
|
|
98
99
|
```
|
|
99
100
|
|
|
100
101
|
Get an API key at [cycls.com](https://cycls.com).
|
|
@@ -4,9 +4,9 @@ cycls/default-theme/assets/index-B0ZKcm_V.css,sha256=wK9-NhEB8xPcN9Zv69zpOcfGTlF
|
|
|
4
4
|
cycls/default-theme/assets/index-D5EDcI4J.js,sha256=sN4qRcAXa7DBd9JzmVcCoCwH4l8cNCM-U9QGUjBvWSo,1346506
|
|
5
5
|
cycls/default-theme/index.html,sha256=bM-yW_g0cGrV40Q5yY3ccY0fM4zI1Wuu5I8EtGFJIxs,828
|
|
6
6
|
cycls/dev-theme/index.html,sha256=QJBHkdNuMMiwQU7o8dN8__8YQeQB45D37D-NCXIWB2Q,11585
|
|
7
|
-
cycls/runtime.py,sha256=
|
|
8
|
-
cycls/sdk.py,sha256=
|
|
7
|
+
cycls/runtime.py,sha256=LgOrQ6Arh-GWSJqckfra-CvjeSekTvGvjHOBxu7JTQQ,21408
|
|
8
|
+
cycls/sdk.py,sha256=6oRKP44TJN9HKdNw9OYzDlZFDUMUhoCMt8TEwyu26dI,7368
|
|
9
9
|
cycls/web.py,sha256=3M3qaWTNY3dpgd7Vq5aXREp-cIFsHrDqBQ1YkGrOaUk,4659
|
|
10
|
-
cycls-0.0.2.
|
|
11
|
-
cycls-0.0.2.
|
|
12
|
-
cycls-0.0.2.
|
|
10
|
+
cycls-0.0.2.70.dist-info/METADATA,sha256=CNYz8lGGGn3lIutuVV4oIy_6e0q4eg8ghkNIErNt5p4,8008
|
|
11
|
+
cycls-0.0.2.70.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
12
|
+
cycls-0.0.2.70.dist-info/RECORD,,
|
|
File without changes
|