cycls 0.0.2.18__py3-none-any.whl → 0.0.2.21__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/cycls.py CHANGED
@@ -10,6 +10,8 @@ import inspect
10
10
  import logging
11
11
  logging.basicConfig(level=logging.ERROR)
12
12
 
13
+ O = lambda x,y: print(f"✦/✧ {str(x).ljust(11)} | {y}")
14
+
13
15
  import os
14
16
  current_dir = os.path.dirname(os.path.abspath(__file__))
15
17
  key_path = os.path.join(current_dir, 'tuns')
@@ -20,6 +22,7 @@ class Message(BaseModel):
20
22
  id: str
21
23
  history: Optional[List[Dict[str, str]]] = None
22
24
 
25
+ #!
23
26
  def find_available_port(start_port):
24
27
  port = start_port
25
28
  while True:
@@ -29,42 +32,40 @@ def find_available_port(start_port):
29
32
  port += 1
30
33
 
31
34
  import asyncssh, asyncio
32
-
33
35
  async def create_ssh_tunnel(x,y,z='tuns.sh'):
34
36
  try:
35
37
  async with asyncssh.connect(z,client_keys=[key_path],known_hosts=None) as conn:
36
38
  listener = await conn.forward_remote_port(x, 80, 'localhost', y)
37
- print("✦/✧","tunnel | open\n")
39
+ O("tunnel","open");print(" ")
38
40
  await listener.wait_closed()
41
+ O("tunnel", "closed")
39
42
  except (OSError, asyncssh.Error) as e:
40
- print("✦/✧",f"tunnel disconnected: {e}")
43
+ O("tunnel",f"disconnected ({e})")
41
44
 
42
- def register(handles, network, url, mode):
45
+ def register(handles, net, url, email):
43
46
  try:
44
47
  with httpx.Client() as client:
45
- response = client.post(f"{network}/register", json={"handles":handles, "url":url, "mode":mode})
48
+ response = client.post(f"{net}/register", json={"handles":handles, "url":url, "email":email})
46
49
  if response.status_code==200:
47
50
  data = (response.json()).get("content")
48
- for i in data:print(f"✦/✧ {i[0]} | {network}/{i[1]}")
51
+ for i in data:
52
+ O(i[0],f"{net}/{i[1]}")
49
53
  else:
50
54
  print("✦/✧ failed to register ⚠️")
51
55
  except Exception as e:
52
56
  print(f"An error occurred: {e}")
53
57
 
54
- async def run_server(x,y):
55
- config = uvicorn.Config(x, host="127.0.0.1", port=y, log_level="error") # loop="asyncio"
56
- server = uvicorn.Server(config)
57
- await server.serve()
58
-
59
58
  class Cycls:
60
- def __init__(self, url="", network="https://cycls.com", port=find_available_port(8001)):
59
+ def __init__(self, url="", net="https://cycls.com", port=find_available_port(8001), email=None):
61
60
  import uuid
62
61
  self.subdomain = str(uuid.uuid4())[:8]
63
62
  self.server = FastAPI()
64
- self.network = network
63
+ self.net = net
65
64
  self.port = port
66
65
  self.url = url
67
66
  self.apps = {}
67
+ self.prod = False
68
+ self.email = email
68
69
 
69
70
  def __call__(self, handle):
70
71
  def decorator(func):
@@ -75,13 +76,20 @@ class Cycls:
75
76
  def sync_wrapper(*args, **kwargs):
76
77
  return StreamingResponse(func(*args, **kwargs))
77
78
  wrapper = async_wrapper if inspect.iscoroutinefunction(func) else sync_wrapper
78
- self.apps["@"+handle] = wrapper
79
+
80
+ if self.url != "": self.prod=True #!
81
+ if not self.prod:
82
+ self.apps[handle + "-dev"] = wrapper
83
+ else:
84
+ self.apps[handle] = wrapper
85
+
79
86
  return wrapper
80
87
  return decorator
81
88
 
82
89
  async def gateway(self, request: Request):
83
90
  data = await request.json()
84
91
  handle = data.get('handle')
92
+ print(handle,self.apps)
85
93
  if handle in self.apps:
86
94
  func = self.apps[handle]
87
95
  message = Message(**data)
@@ -89,36 +97,40 @@ class Cycls:
89
97
  return {"error": "Handle not found"}
90
98
 
91
99
  def push(self):
92
- self.server.post("/gateway")(self.gateway)
93
- asyncio.run(self.publish())
94
-
95
- async def publish(self):
96
- prod=False
97
- if self.url != "":
98
- prod=True
99
-
100
- print(f"✦/✧ port | {self.port}")
101
- if prod:
102
- print("✦/✧",f"production mode | url: {self.url}")
103
- register(list(self.apps.keys()), self.network, self.url+"/gateway", "prod")
100
+ if self.email:
101
+ O("email",self.email)
102
+ O("port",self.port)
103
+ if self.prod:
104
+ O("mode",f"production @ {self.url}")
105
+ register(list(self.apps.keys()), self.net, self.url+"/gateway", self.email)
104
106
  else:
105
107
  self.url = f"http://{self.subdomain}-cycls.tuns.sh"
106
- print("✦/✧","mode | development")
107
- # print("✦/✧",f"url {self.url}")
108
- register(list(self.apps.keys()), self.network, self.url+"/gateway", "dev")
109
- t1 = asyncio.create_task(create_ssh_tunnel(f"{self.subdomain}-cycls", self.port))
108
+ O("mode","development")
109
+ O("docs","for more information, visit https://github.com/Cycls/cycls-py")
110
+ register(list(self.apps.keys()), self.net, self.url+"/gateway", self.email)
110
111
 
111
- t2 = asyncio.create_task(run_server(self.server,self.port))
112
-
113
- try:
114
- if not prod:
115
- await asyncio.gather(t1, t2)
112
+ self.server.post("/gateway")(self.gateway)
113
+ @self.server.on_event("startup")
114
+ def startup_event():
115
+ if self.prod:
116
+ pass
116
117
  else:
117
- await asyncio.gather(t2)
118
+ asyncio.create_task(create_ssh_tunnel(f"{self.subdomain}-cycls", self.port))
119
+ try:
120
+ uvicorn.run(self.server, host="127.0.0.1", port=self.port, log_level="error")
118
121
  except KeyboardInterrupt:
119
- # print("🔥")
120
- tasks = asyncio.all_tasks()
121
- for task in tasks:
122
- task.cancel()
122
+ print(" ");O("exit","done")
123
123
 
124
+ async def call(self, handle, content):
125
+ data = {"handle":handle, "content":content, "session":{}, "agent":"yes"}
126
+ try:
127
+ url = f"{self.net}/stream/"
128
+ async with httpx.AsyncClient(timeout=20) as client, client.stream("POST", url, json=data) as response:
129
+ if response.status_code != 200:
130
+ print("http error")
131
+ async for token in response.aiter_text():
132
+ yield token
133
+ except Exception as e:
134
+ print("Exception", e)
135
+
124
136
  # poetry publish --build
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.1
2
+ Name: cycls
3
+ Version: 0.0.2.21
4
+ Summary: Cycls SDK
5
+ Author: Mohammed Jamal
6
+ Author-email: mj@cycls.com
7
+ Requires-Python: >=3.8,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.8
10
+ Classifier: Programming Language :: Python :: 3.9
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Requires-Dist: asyncssh (>=2.14.2,<3.0.0)
15
+ Requires-Dist: fastapi (>=0.111.0,<0.112.0)
16
+ Requires-Dist: httpx (>=0.27.0,<0.28.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ </br></br>
20
+ <p align="center">
21
+ <picture>
22
+ <source media="(prefers-color-scheme: dark)" srcset="https://cycls.com/static/assets/logo-gold.svg">
23
+ <source media="(prefers-color-scheme: light)" srcset="https://cycls.com/static/assets/logo.svg">
24
+ <img alt="Cycls" src="https://cycls.com/static/assets/logo.svg" width="150">
25
+ </picture>
26
+ </p>
27
+ </br></br>
28
+
29
+ <div align="center">
30
+ <a href="https://pypi.org/project/cycls/" target="_blank" rel="noopener noreferrer">
31
+ <img loading="lazy" src="https://img.shields.io/pypi/v/cycls.svg" alt="PyPI" class="img_ev3q" style="display: inline;">
32
+ </a>
33
+ <a href="https://discord.gg/BMnaMatDC7" target="_blank" rel="noopener noreferrer">
34
+ <img loading="lazy" src="https://img.shields.io/discord/1175782747164389466" alt="Discord" class="img_ev3q" style="display: inline;">
35
+ </a>
36
+ </div>
37
+
38
+ </br>
39
+
40
+ ```sh
41
+ pip install cycls
42
+ ```
43
+
44
+ # Apps ✦
45
+ Instantly publish and share AI apps
46
+
47
+ ```py
48
+ from cycls import Cycls
49
+
50
+ cycls = Cycls()
51
+
52
+ @cycls("@spark")
53
+ def app(x):
54
+ return x.content + "from spark"
55
+
56
+ cycls.push()
57
+ ```
58
+ `cycls.push()` will then publish the app `@spark` on [cycls.com/@spark](https://cycls.com/@spark)
59
+ ## Async Apps
60
+ For performance, make the function asynchronous. The following is an async app with message `history` and session `id`
61
+ ```py
62
+ from cycls import Cycls
63
+
64
+ cycls = Cycls()
65
+
66
+ @cycls("@spark")
67
+ async def app(x):
68
+ print(x.history, x.id)
69
+ return x.content + "from spark"
70
+
71
+ cycls.push()
72
+ ```
73
+
74
+ # Agents ✧
75
+ Call any public app as an agent, see [explore](https://explore.cycls.com)
76
+ ```py
77
+ from cycls import Cycls
78
+
79
+ cycls = Cycls()
80
+
81
+ @cycls("@spark")
82
+ async def app(x):
83
+ return cycls.call("@groq",
84
+ x.content)
85
+
86
+ cycls.push()
87
+ ```
88
+
89
+ ### Try it live
90
+ - [cycls.com/@groq](https://cycls.com/@groq) | [groq.py](https://github.com/Cycls/examples/blob/main/groq.py)
91
+ - [cycls.com/@openai](https://cycls.com/@openai) | [openai.py](https://github.com/Cycls/examples/blob/main/openai.py)
92
+
@@ -0,0 +1,6 @@
1
+ cycls/__init__.py,sha256=HPPQikt_Trbc4s8XyEOeOjB_JRWXlXIkJuh762nUM_g,24
2
+ cycls/cycls.py,sha256=Qz5O27nZrQjrwIwiBbr_VuiptUKUzVi-_oAwdGgVeyM,4826
3
+ cycls/tuns,sha256=CT8olxDKOM0cY6r_bbSGtnWyEmEYFVDVHj9ug3gbYHk,1843
4
+ cycls-0.0.2.21.dist-info/METADATA,sha256=DMY3hJaeDS8rOHOZ7Lj8s-ckDNt3mZ4lpmBI7oJyctw,2593
5
+ cycls-0.0.2.21.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
6
+ cycls-0.0.2.21.dist-info/RECORD,,
@@ -1,68 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: cycls
3
- Version: 0.0.2.18
4
- Summary: Cycls SDK
5
- Author: Mohammed Jamal
6
- Author-email: mj@cycls.com
7
- Requires-Python: >=3.8,<4.0
8
- Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.8
10
- Classifier: Programming Language :: Python :: 3.9
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Classifier: Programming Language :: Python :: 3.12
14
- Requires-Dist: asyncssh (>=2.14.2,<3.0.0)
15
- Requires-Dist: fastapi (>=0.111.0,<0.112.0)
16
- Requires-Dist: httpx (>=0.27.0,<0.28.0)
17
- Description-Content-Type: text/markdown
18
-
19
- </br></br><p align="center"><img src="https://cycls.com/static/assets/favicon.svg" alt="Cycls"></p></br>
20
-
21
- <div align="center">
22
- <a href="https://pypi.org/project/cycls/" target="_blank" rel="noopener noreferrer">
23
- <img loading="lazy" src="https://img.shields.io/pypi/v/cycls.svg" alt="PyPI" class="img_ev3q" style="display: inline;">
24
- </a>
25
- <a href="https://discord.gg/BMnaMatDC7" target="_blank" rel="noopener noreferrer">
26
- <img loading="lazy" src="https://img.shields.io/discord/1175782747164389466" alt="Discord" class="img_ev3q" style="display: inline;">
27
- </a>
28
- </div>
29
-
30
- </br>
31
-
32
- ```sh
33
- pip install cycls
34
- ```
35
-
36
- ```py
37
- from cycls import Cycls
38
-
39
- cycls = Cycls()
40
-
41
- # sync app on https://cycls.com/@spark
42
- @cycls("spark")
43
- def spark_app(message):
44
- print("history", message.history)
45
- print("session id", message.id)
46
- return message.content + "from spark"
47
-
48
- # async app on https://cycls.com/@cake
49
- @cycls("cake")
50
- async def cake_app(message):
51
- print("history", message.history)
52
- print("session id", message.id)
53
- return message.content + "from cake"
54
-
55
- # publish to https://cycls.com
56
- cycls.push()
57
- ```
58
-
59
- Return a string. Supports markdown. Supports generators for streaming responses.
60
-
61
- try it live
62
- - https://cycls.com/@groq
63
- - https://cycls.com/@openai
64
-
65
- code examples
66
- - https://github.com/Cycls/examples/blob/main/groq.py
67
- - https://github.com/Cycls/examples/blob/main/openai.py
68
-
@@ -1,6 +0,0 @@
1
- cycls/__init__.py,sha256=HPPQikt_Trbc4s8XyEOeOjB_JRWXlXIkJuh762nUM_g,24
2
- cycls/cycls.py,sha256=-jm2IM_yNlZoUYmcDhqTcz_Umiwf7jQLBq_s6pbpAQM,4304
3
- cycls/tuns,sha256=CT8olxDKOM0cY6r_bbSGtnWyEmEYFVDVHj9ug3gbYHk,1843
4
- cycls-0.0.2.18.dist-info/METADATA,sha256=bpAtiAC5ACoddUPX7EYzzikKgT-IZf0f_CyJCGqFasA,2018
5
- cycls-0.0.2.18.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
6
- cycls-0.0.2.18.dist-info/RECORD,,