aicosmos-client 0.0.5__tar.gz → 0.0.6__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aicosmos_client
3
- Version: 0.0.5
3
+ Version: 0.0.6
4
4
  Summary: client for AICosmos platform
5
5
  Project-URL: Homepage, https://github.com/pypa/sampleproject
6
6
  Project-URL: Issues, https://github.com/pypa/sampleproject/issues
@@ -28,7 +28,10 @@ from aicosmos_client.client import AICosmosClient
28
28
 
29
29
  # login
30
30
  client = AICosmosClient(
31
- base_url="http://xxx.xxx", username="xxx", password="xxxxxx"
31
+ base_url="https://aicosmos.ai/api",
32
+ username="xxx",
33
+ password="xxx",
34
+ auto_trust=True,
32
35
  )
33
36
 
34
37
  # create a new session
@@ -62,5 +65,6 @@ To show that the client is enough to build an application, we offer you an comma
62
65
  ```Python
63
66
  from aicosmos_client.cli import AICosmosCLI
64
67
 
68
+ # url: https://aicosmos.ai/api
65
69
  AICosmosCLI().run()
66
70
  ```
@@ -12,7 +12,10 @@ from aicosmos_client.client import AICosmosClient
12
12
 
13
13
  # login
14
14
  client = AICosmosClient(
15
- base_url="http://xxx.xxx", username="xxx", password="xxxxxx"
15
+ base_url="https://aicosmos.ai/api",
16
+ username="xxx",
17
+ password="xxx",
18
+ auto_trust=True,
16
19
  )
17
20
 
18
21
  # create a new session
@@ -46,5 +49,6 @@ To show that the client is enough to build an application, we offer you an comma
46
49
  ```Python
47
50
  from aicosmos_client.cli import AICosmosCLI
48
51
 
52
+ # url: https://aicosmos.ai/api
49
53
  AICosmosCLI().run()
50
54
  ```
@@ -0,0 +1,178 @@
1
+ import os
2
+ import socket
3
+ import ssl
4
+ from urllib.parse import urlparse
5
+
6
+ import certifi
7
+ import requests
8
+ from requests.adapters import HTTPAdapter
9
+
10
+
11
+ class SSLAdapter(HTTPAdapter):
12
+ """HTTPS adapter that allows dynamic CA bundle injection."""
13
+
14
+ def __init__(self, cafile=None, *args, **kwargs):
15
+ self.cafile = cafile
16
+ super().__init__(*args, **kwargs)
17
+
18
+ def init_poolmanager(self, *args, **kwargs):
19
+ context = ssl.create_default_context()
20
+ context.load_verify_locations(cafile=certifi.where())
21
+ if self.cafile and os.path.exists(self.cafile):
22
+ context.load_verify_locations(cafile=self.cafile)
23
+ kwargs["ssl_context"] = context
24
+ return super().init_poolmanager(*args, **kwargs)
25
+
26
+
27
+ class AICosmosClient:
28
+ def __init__(
29
+ self,
30
+ base_url: str,
31
+ username: str,
32
+ password: str,
33
+ certs_dir: str = None,
34
+ auto_trust: bool = False,
35
+ ):
36
+ """
37
+ :param base_url: API base URL, e.g. 'https://aicosmos.ai/api'
38
+ :param username: Username for login
39
+ :param password: Password for login
40
+ :param certs_dir: Directory for storing trusted self-signed certs
41
+ :param auto_trust: If True, will automatically trust self-signed certs
42
+ """
43
+ self.base_url = base_url.rstrip("/")
44
+ self.username = username
45
+ self.password = password
46
+ self.access_token: str = None
47
+ self.auto_trust = auto_trust
48
+
49
+ host = urlparse(self.base_url).hostname
50
+ self.certs_dir = certs_dir or os.path.join(
51
+ os.path.expanduser("~"), ".aicosmos", "certs"
52
+ )
53
+ os.makedirs(self.certs_dir, exist_ok=True)
54
+ self.cert_file = os.path.join(self.certs_dir, f"{host}.pem")
55
+
56
+ self.session = requests.Session()
57
+ self.session.mount("https://", SSLAdapter(cafile=self.cert_file))
58
+
59
+ self._login()
60
+
61
+ def _fetch_server_cert(self, hostname, port=443):
62
+ """Download server's SSL certificate and save locally."""
63
+ pem_path = self.cert_file
64
+ conn = socket.create_connection((hostname, port))
65
+ context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
66
+ context.check_hostname = False
67
+ context.verify_mode = ssl.CERT_NONE
68
+ with context.wrap_socket(conn, server_hostname=hostname) as sock:
69
+ der_cert = sock.getpeercert(True)
70
+ pem_cert = ssl.DER_cert_to_PEM_cert(der_cert)
71
+ with open(pem_path, "w") as f:
72
+ f.write(pem_cert)
73
+ return pem_path
74
+
75
+ def _robust_request(self, method, url, **kwargs):
76
+ try:
77
+ return self.session.request(method, url, **kwargs)
78
+ except requests.exceptions.SSLError as e:
79
+ if not self.auto_trust:
80
+ raise RuntimeError(
81
+ f"SSL verification failed for {url}. "
82
+ f"Set auto_trust=True to accept and store the server's certificate."
83
+ ) from e
84
+ host = urlparse(self.base_url).hostname
85
+ self._fetch_server_cert(host)
86
+ self.session.mount("https://", SSLAdapter(cafile=self.cert_file))
87
+ return self.session.request(method, url, **kwargs)
88
+
89
+ def _login(self):
90
+ login_data = {
91
+ "username": self.username,
92
+ "password": self.password,
93
+ "grant_type": "password",
94
+ }
95
+ headers = {"Content-Type": "application/x-www-form-urlencoded"}
96
+ response = self._robust_request(
97
+ "POST", f"{self.base_url}/user/login", data=login_data, headers=headers
98
+ )
99
+ if response.status_code == 200:
100
+ token_data = response.json()
101
+ self.access_token = token_data["access_token"]
102
+ else:
103
+ raise ValueError(f"Login failed: {response.status_code} {response.text}")
104
+
105
+ def _get_auth_headers(self):
106
+ if not self.access_token:
107
+ raise ValueError("Not logged in")
108
+ return {
109
+ "Authorization": f"Bearer {self.access_token}",
110
+ "Content-Type": "application/json",
111
+ }
112
+
113
+ def _get_session_status(self, session_id):
114
+ response = self._robust_request(
115
+ "GET",
116
+ f"{self.base_url}/sessions/{session_id}/status",
117
+ headers=self._get_auth_headers(),
118
+ )
119
+ if response.status_code == 200:
120
+ return response.json()
121
+ else:
122
+ raise ValueError(f"Status code: {response.status_code}")
123
+
124
+ def create_session(self):
125
+ response = self._robust_request(
126
+ "POST",
127
+ f"{self.base_url}/sessions/create",
128
+ headers=self._get_auth_headers(),
129
+ )
130
+ if response.status_code == 200:
131
+ return response.json()["session_id"]
132
+ else:
133
+ raise ValueError(f"Status code: {response.status_code}")
134
+
135
+ def delete_session(self, session_id: str):
136
+ response = self._robust_request(
137
+ "DELETE",
138
+ f"{self.base_url}/sessions/{session_id}",
139
+ headers=self._get_auth_headers(),
140
+ )
141
+ if response.status_code != 200:
142
+ raise ValueError(f"Status code: {response.status_code}")
143
+
144
+ def get_my_sessions(self):
145
+ response = self._robust_request(
146
+ "GET",
147
+ f"{self.base_url}/sessions/my_sessions",
148
+ headers=self._get_auth_headers(),
149
+ )
150
+ if response.status_code == 200:
151
+ sessions = response.json()
152
+ self.active_sessions = sessions
153
+ return [
154
+ {
155
+ "session_id": s["session_id"],
156
+ "title": s["environment_info"].get("title"),
157
+ }
158
+ for s in sessions
159
+ ]
160
+ else:
161
+ raise ValueError(f"Status code: {response.status_code}")
162
+
163
+ def get_session_history(self, session_id: str):
164
+ session = self._get_session_status(session_id)
165
+ return session.get("conversation", [])
166
+
167
+ def chat(self, session_id: str, prompt: str):
168
+ data = {"user_input": prompt, "session_id": session_id}
169
+ response = self._robust_request(
170
+ "POST",
171
+ f"{self.base_url}/chat",
172
+ json=data,
173
+ headers=self._get_auth_headers(),
174
+ )
175
+ if response.status_code == 200:
176
+ return response.json()["conversation_history"]
177
+ else:
178
+ raise ValueError(f"Status code: {response.status_code}")
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "aicosmos_client"
7
- version = "0.0.5"
7
+ version = "0.0.6"
8
8
  authors = [{ name = "Example Author", email = "author@example.com" }]
9
9
  description = "client for AICosmos platform"
10
10
  readme = "README.md"
@@ -1,132 +0,0 @@
1
- import requests
2
-
3
-
4
- class AICosmosClient:
5
- def __init__(self, base_url: str, username: str, password: str):
6
- self.session = requests.Session()
7
- self.base_url = base_url
8
- self.username: str = username
9
- self.password: str = password
10
- self.access_token: str = None
11
-
12
- self._login()
13
-
14
- def _login(self):
15
- login_data = {
16
- "username": self.username,
17
- "password": self.password,
18
- "grant_type": "password",
19
- }
20
- headers = {"Content-Type": "application/x-www-form-urlencoded"}
21
- try:
22
- response = self.session.post(
23
- f"{self.base_url}/user/login", data=login_data, headers=headers
24
- )
25
- if response.status_code == 200:
26
- token_data = response.json()
27
- self.access_token = token_data["access_token"]
28
- return
29
- else:
30
- raise ValueError(f"Status code: {response.status_code}")
31
- except Exception as e:
32
- raise ValueError(f"Error: {e}")
33
-
34
- def _get_auth_headers(self):
35
- if not self.access_token:
36
- raise ValueError("Not logged in")
37
- return {
38
- "Authorization": f"Bearer {self.access_token}",
39
- "Content-Type": "application/json",
40
- }
41
-
42
- def _get_session_status(self, session_id):
43
- try:
44
- response = self.session.get(
45
- f"{self.base_url}/sessions/{session_id}/status",
46
- headers=self._get_auth_headers(),
47
- )
48
- success = response.status_code == 200
49
- if success:
50
- return response.json()
51
- else:
52
- raise ValueError(f"Status code: {response.status_code}")
53
- except Exception as e:
54
- raise ValueError(f"Error: {e}")
55
-
56
- def create_session(self):
57
- if not self.access_token:
58
- raise ValueError("Not logged in")
59
- try:
60
- response = self.session.post(
61
- f"{self.base_url}/sessions/create", headers=self._get_auth_headers()
62
- )
63
- if response.status_code == 200:
64
- response_json = response.json()
65
- return response_json["session_id"]
66
- else:
67
- raise ValueError(f"Status code: {response.status_code}")
68
- except Exception as e:
69
- raise ValueError(f"Error: {e}")
70
-
71
- def delete_session(self, session_id: str):
72
- if not self.access_token:
73
- raise ValueError("Not logged in")
74
- try:
75
- response = self.session.delete(
76
- f"{self.base_url}/sessions/{session_id}",
77
- headers=self._get_auth_headers(),
78
- )
79
- if response.status_code == 200:
80
- return
81
- else:
82
- raise ValueError(f"Status code: {response.status_code}")
83
- except Exception as e:
84
- raise ValueError(f"Error: {e}")
85
-
86
- def get_my_sessions(self):
87
- if not self.access_token:
88
- raise ValueError("Not logged in")
89
- try:
90
- response = self.session.get(
91
- f"{self.base_url}/sessions/my_sessions",
92
- headers=self._get_auth_headers(),
93
- )
94
- if response.status_code == 200:
95
- sessions = response.json()
96
- self.active_sessions = sessions
97
- return [
98
- {
99
- "session_id": session["session_id"],
100
- "title": session["environment_info"].get("title", None),
101
- }
102
- for session in sessions
103
- ]
104
- else:
105
- raise ValueError(f"Status code: {response.status_code}")
106
- except Exception as e:
107
- raise ValueError(f"Error: {e}")
108
-
109
- def get_session_history(self, session_id: str):
110
- session = self._get_session_status(session_id)
111
- return session.get("conversation", [])
112
-
113
- def chat(self, session_id: str, prompt: str):
114
- if not self.access_token:
115
- raise ValueError("Not logged in")
116
- data = {
117
- "user_input": prompt,
118
- "session_id": session_id,
119
- }
120
- try:
121
- response = self.session.post(
122
- f"{self.base_url}/chat",
123
- json=data,
124
- headers=self._get_auth_headers(),
125
- )
126
- success = response.status_code == 200
127
- if success:
128
- return response.json()["conversation_history"]
129
- else:
130
- raise ValueError(f"Status code: {response.status_code}")
131
- except Exception as e:
132
- raise ValueError(f"Error: {e}")
File without changes