cycls 0.0.2.19__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,7 +22,7 @@ class Message(BaseModel):
20
22
  id: str
21
23
  history: Optional[List[Dict[str, str]]] = None
22
24
 
23
- # too dirty
25
+ #!
24
26
  def find_available_port(start_port):
25
27
  port = start_port
26
28
  while True:
@@ -34,36 +36,36 @@ 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
51
  for i in data:
49
- if i[0]=="new":
50
- print(f"✦/✧ {i[0]} | {network}/{i[1]}")
51
- else:
52
- print(f"✦/✧ {i[0]} | {network}/{i[1]}")
52
+ O(i[0],f"{net}/{i[1]}")
53
53
  else:
54
54
  print("✦/✧ failed to register ⚠️")
55
55
  except Exception as e:
56
56
  print(f"An error occurred: {e}")
57
57
 
58
58
  class Cycls:
59
- 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):
60
60
  import uuid
61
61
  self.subdomain = str(uuid.uuid4())[:8]
62
62
  self.server = FastAPI()
63
- self.network = network
63
+ self.net = net
64
64
  self.port = port
65
65
  self.url = url
66
66
  self.apps = {}
67
+ self.prod = False
68
+ self.email = email
67
69
 
68
70
  def __call__(self, handle):
69
71
  def decorator(func):
@@ -74,13 +76,20 @@ class Cycls:
74
76
  def sync_wrapper(*args, **kwargs):
75
77
  return StreamingResponse(func(*args, **kwargs))
76
78
  wrapper = async_wrapper if inspect.iscoroutinefunction(func) else sync_wrapper
77
- 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
+
78
86
  return wrapper
79
87
  return decorator
80
88
 
81
89
  async def gateway(self, request: Request):
82
90
  data = await request.json()
83
91
  handle = data.get('handle')
92
+ print(handle,self.apps)
84
93
  if handle in self.apps:
85
94
  func = self.apps[handle]
86
95
  message = Message(**data)
@@ -88,31 +97,40 @@ class Cycls:
88
97
  return {"error": "Handle not found"}
89
98
 
90
99
  def push(self):
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)
106
+ else:
107
+ self.url = f"http://{self.subdomain}-cycls.tuns.sh"
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)
111
+
91
112
  self.server.post("/gateway")(self.gateway)
92
113
  @self.server.on_event("startup")
93
114
  def startup_event():
94
- asyncio.create_task(create_ssh_tunnel(f"{self.subdomain}-cycls", self.port))
95
-
96
- self.publish()
97
-
115
+ if self.prod:
116
+ pass
117
+ else:
118
+ asyncio.create_task(create_ssh_tunnel(f"{self.subdomain}-cycls", self.port))
98
119
  try:
99
120
  uvicorn.run(self.server, host="127.0.0.1", port=self.port, log_level="error")
100
121
  except KeyboardInterrupt:
101
- print(f"\n✦/✧ exit | done")
102
-
103
- def publish(self):
104
- # prod catch is too dirty
105
- prod=False
106
- if self.url != "":
107
- prod=True
122
+ print(" ");O("exit","done")
108
123
 
109
- print(f"✦/✧ port | {self.port}")
110
- if prod:
111
- print("✦/✧",f"production mode | url: {self.url}")
112
- register(list(self.apps.keys()), self.network, self.url+"/gateway", "prod")
113
- else:
114
- self.url = f"http://{self.subdomain}-cycls.tuns.sh"
115
- print("✦/✧","mode | development")
116
- register(list(self.apps.keys()), self.network, self.url+"/gateway", "dev")
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)
117
135
 
118
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.19
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=Y_6IiHAPgnZI9xZEzR4mzCwjVd6awFrbY5USUjX6jW4,4138
3
- cycls/tuns,sha256=CT8olxDKOM0cY6r_bbSGtnWyEmEYFVDVHj9ug3gbYHk,1843
4
- cycls-0.0.2.19.dist-info/METADATA,sha256=BmoAPVK4piZoaWvDo7YB-cpddkvpiJzdslWCiK-6QVE,2018
5
- cycls-0.0.2.19.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
6
- cycls-0.0.2.19.dist-info/RECORD,,