regolo 1.0.0__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.
regolo-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 regolo.ai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
regolo-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.2
2
+ Name: regolo
3
+ Version: 1.0.0
4
+ Summary: Simple client to interact with regolo.ai
5
+ Author-email: "regolo.ai" <dev@regolo.ai>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 regolo.ai
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/regolo-ai/python-client
29
+ Keywords: regolo,llm,chat
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Programming Language :: Python
32
+ Classifier: Programming Language :: Python :: 3
33
+ Requires-Python: >=3.12
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Requires-Dist: httpx~=0.28.1
37
+ Requires-Dist: pydantic~=2.10.6
38
+ Requires-Dist: json_repair~=0.35.0
39
+
40
+ # **Regolo.ai Python Client**
41
+
42
+ A simple Python client for interacting for **Regolo.ai's** LLM-based API.
43
+
44
+ ## **Installation**
45
+ Ensure you have the `regolo` module installed. If not, install it using:
46
+
47
+ ```bash
48
+ pip install regolo
49
+ ```
50
+
51
+ # **Basic Usage**
52
+
53
+ ## **1. Import the regolo module**
54
+
55
+ ```python
56
+ import regolo
57
+ ```
58
+ ## **2. Set Up Default API Key and Model**
59
+
60
+ To avoid manually passing the API key and model in every request, you can set them globally:
61
+
62
+ ```python
63
+ regolo.default_key = "<EXAMPLE_KEY>"
64
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
65
+ ```
66
+
67
+ This ensures that all `RegoloClient` instances and static functions will
68
+ use the specified API key and model.
69
+
70
+ Still, you can create run methods by passing model and key directly.
71
+
72
+ ## **3. Perform a basic request**
73
+
74
+ ### Completion:
75
+ ```python
76
+ print(regolo.static_completions(prompt="Tell me something about Rome."))
77
+ ```
78
+
79
+ ### Chat_completion
80
+ ```python
81
+ print(regolo.static_chat_completions(messages=[{"role": "user", "content": "Tell me something about rome"}]))
82
+ ```
83
+
84
+ ---
85
+
86
+ # **Other usages**
87
+
88
+ ## **Handling streams**
89
+
90
+
91
+ **With full output:**
92
+
93
+ ```python
94
+ import regolo
95
+ regolo.default_key = "<EXAMPLE_KEY>"
96
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
97
+
98
+ # Completions
99
+
100
+ client = regolo.RegoloClient()
101
+ response = client.completions("Tell me about Rome in a concise manner", full_output=True, stream=True)
102
+
103
+ while True:
104
+ try:
105
+ print(next(response))
106
+ except StopIteration:
107
+ break
108
+
109
+ # Chat completions
110
+
111
+ client = regolo.RegoloClient()
112
+ response = client.run_chat(user_prompt="Tell me about Rome in a concise manner", full_output=True, stream=True)
113
+
114
+
115
+ while True:
116
+ try:
117
+ print(next(response))
118
+ except StopIteration:
119
+ break
120
+ ```
121
+
122
+ **Without full output:**
123
+
124
+ ```python
125
+ import regolo
126
+ regolo.default_key = "<EXAMPLE_KEY>"
127
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
128
+
129
+ # Completions
130
+
131
+ client = regolo.RegoloClient()
132
+ response = client.completions("Tell me about Rome in a concise manner", full_output=True, stream=True)
133
+
134
+ while True:
135
+ try:
136
+ print(next(response), end='', flush=True)
137
+ except StopIteration:
138
+ break
139
+
140
+ # Chat completions
141
+
142
+ client = regolo.RegoloClient()
143
+ response = client.run_chat(user_prompt="Tell me about Rome in a concise manner", full_output=True, stream=True)
144
+
145
+ while True:
146
+ try:
147
+ res = next(response)
148
+ if res[0]:
149
+ print(res[0] + ":")
150
+ print(res[1], end="", flush=True)
151
+ except StopIteration:
152
+ break
153
+ ```
154
+
155
+ ## **Handling chat through add_prompt_to_chat()**
156
+
157
+ ```python
158
+ import regolo
159
+
160
+ client = regolo.RegoloClient()
161
+
162
+ # Make a request
163
+
164
+ client.add_prompt_to_chat(role="user", prompt="Tell me about rome!")
165
+
166
+ print(client.run_chat())
167
+
168
+ # Continue the conversation
169
+
170
+ client.add_prompt_to_chat(role="user", prompt="Tell me something more about it!")
171
+
172
+ print(client.run_chat())
173
+
174
+ # You can print the whole conversation if needed
175
+
176
+ print(print(client.instance.get_conversation()))
177
+ ```
178
+
179
+ It is to consider that using the user_prompt parameter in run_chat() is equivalent to adding a prompt with role=user
180
+ through add_prompt_to_chat().
regolo-1.0.0/README.md ADDED
@@ -0,0 +1,141 @@
1
+ # **Regolo.ai Python Client**
2
+
3
+ A simple Python client for interacting for **Regolo.ai's** LLM-based API.
4
+
5
+ ## **Installation**
6
+ Ensure you have the `regolo` module installed. If not, install it using:
7
+
8
+ ```bash
9
+ pip install regolo
10
+ ```
11
+
12
+ # **Basic Usage**
13
+
14
+ ## **1. Import the regolo module**
15
+
16
+ ```python
17
+ import regolo
18
+ ```
19
+ ## **2. Set Up Default API Key and Model**
20
+
21
+ To avoid manually passing the API key and model in every request, you can set them globally:
22
+
23
+ ```python
24
+ regolo.default_key = "<EXAMPLE_KEY>"
25
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
26
+ ```
27
+
28
+ This ensures that all `RegoloClient` instances and static functions will
29
+ use the specified API key and model.
30
+
31
+ Still, you can create run methods by passing model and key directly.
32
+
33
+ ## **3. Perform a basic request**
34
+
35
+ ### Completion:
36
+ ```python
37
+ print(regolo.static_completions(prompt="Tell me something about Rome."))
38
+ ```
39
+
40
+ ### Chat_completion
41
+ ```python
42
+ print(regolo.static_chat_completions(messages=[{"role": "user", "content": "Tell me something about rome"}]))
43
+ ```
44
+
45
+ ---
46
+
47
+ # **Other usages**
48
+
49
+ ## **Handling streams**
50
+
51
+
52
+ **With full output:**
53
+
54
+ ```python
55
+ import regolo
56
+ regolo.default_key = "<EXAMPLE_KEY>"
57
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
58
+
59
+ # Completions
60
+
61
+ client = regolo.RegoloClient()
62
+ response = client.completions("Tell me about Rome in a concise manner", full_output=True, stream=True)
63
+
64
+ while True:
65
+ try:
66
+ print(next(response))
67
+ except StopIteration:
68
+ break
69
+
70
+ # Chat completions
71
+
72
+ client = regolo.RegoloClient()
73
+ response = client.run_chat(user_prompt="Tell me about Rome in a concise manner", full_output=True, stream=True)
74
+
75
+
76
+ while True:
77
+ try:
78
+ print(next(response))
79
+ except StopIteration:
80
+ break
81
+ ```
82
+
83
+ **Without full output:**
84
+
85
+ ```python
86
+ import regolo
87
+ regolo.default_key = "<EXAMPLE_KEY>"
88
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
89
+
90
+ # Completions
91
+
92
+ client = regolo.RegoloClient()
93
+ response = client.completions("Tell me about Rome in a concise manner", full_output=True, stream=True)
94
+
95
+ while True:
96
+ try:
97
+ print(next(response), end='', flush=True)
98
+ except StopIteration:
99
+ break
100
+
101
+ # Chat completions
102
+
103
+ client = regolo.RegoloClient()
104
+ response = client.run_chat(user_prompt="Tell me about Rome in a concise manner", full_output=True, stream=True)
105
+
106
+ while True:
107
+ try:
108
+ res = next(response)
109
+ if res[0]:
110
+ print(res[0] + ":")
111
+ print(res[1], end="", flush=True)
112
+ except StopIteration:
113
+ break
114
+ ```
115
+
116
+ ## **Handling chat through add_prompt_to_chat()**
117
+
118
+ ```python
119
+ import regolo
120
+
121
+ client = regolo.RegoloClient()
122
+
123
+ # Make a request
124
+
125
+ client.add_prompt_to_chat(role="user", prompt="Tell me about rome!")
126
+
127
+ print(client.run_chat())
128
+
129
+ # Continue the conversation
130
+
131
+ client.add_prompt_to_chat(role="user", prompt="Tell me something more about it!")
132
+
133
+ print(client.run_chat())
134
+
135
+ # You can print the whole conversation if needed
136
+
137
+ print(print(client.instance.get_conversation()))
138
+ ```
139
+
140
+ It is to consider that using the user_prompt parameter in run_chat() is equivalent to adding a prompt with role=user
141
+ through add_prompt_to_chat().
@@ -0,0 +1,52 @@
1
+ # pyproject.toml
2
+
3
+ [build-system]
4
+ requires = ["setuptools>=75.8.0", "wheel"]
5
+ build-backend = "setuptools.build_meta"
6
+
7
+ [project]
8
+ name = "regolo"
9
+ version = "1.0.0"
10
+ description = "Simple client to interact with regolo.ai"
11
+ readme = "README.md"
12
+ authors = [{ name = "regolo.ai", email = "dev@regolo.ai" }]
13
+ license = { file = "LICENSE" }
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python",
17
+ "Programming Language :: Python :: 3",
18
+ ]
19
+ keywords = ["regolo", "llm", "chat"]
20
+ dependencies = [
21
+ "httpx ~= 0.28.1",
22
+ "pydantic ~= 2.10.6",
23
+ 'json_repair ~= 0.35.0',
24
+ ]
25
+ requires-python = ">=3.12"
26
+
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/regolo-ai/python-client"
30
+
31
+
32
+ [tool.bumpver]
33
+ current_version = "1.0.0"
34
+ version_pattern = "MAJOR.MINOR.PATCH"
35
+ commit_message = "bump version {old_version} -> {new_version}"
36
+ tag_message = "{new_version}"
37
+ tag_scope = "default"
38
+ commit = true
39
+ tag = true
40
+ push = true
41
+
42
+ [tool.bumpver.file_patterns]
43
+ "pyproject.toml" = [
44
+ 'current_version = "{version}"',
45
+ ]
46
+ "README.md" = [
47
+ "{version}",
48
+ "{pep440_version}",
49
+ ]
50
+ "src/*" = [
51
+ 'current_version = "{version}"',
52
+ ]
regolo-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,7 @@
1
+ default_key = None
2
+ default_model = None
3
+
4
+ from regolo.client.regolo_client import RegoloClient
5
+
6
+ static_completions = RegoloClient.static_completions
7
+ static_chat_completions = RegoloClient.static_chat_completions
File without changes
@@ -0,0 +1,396 @@
1
+ import json
2
+ from types import GeneratorType
3
+
4
+ import httpx
5
+ from typing import Any, Dict, List, Optional, Callable
6
+
7
+ from json_repair import json_repair
8
+
9
+ from regolo.instance.regolo_instance import RegoloInstance
10
+ from regolo.instance.structures.conversation_model import Conversation, ConversationLine
11
+ from regolo.keys.keys import KeysHandler
12
+ from regolo.models.models import ModelsHandler
13
+ from typing import TypeAlias
14
+
15
+ import regolo
16
+
17
+ REGOLO_URL = "https://api.regolo.ai"
18
+ COMPLETIONS_URL_PATH = "/v1/completions"
19
+ CHAT_COMPLETIONS_URL_PATH = "/v1/chat/completions"
20
+ timeout = 500
21
+
22
+ Role: TypeAlias = str
23
+ Content: TypeAlias = str
24
+
25
+
26
+ def safe_post(client: httpx.Client,
27
+ url_to_query: str,
28
+ json_to_query: dict = None,
29
+ headers_to_query: dict = None) -> httpx.Response:
30
+ response = client.post(url=url_to_query, json=json_to_query, headers=headers_to_query, timeout=timeout)
31
+ response.raise_for_status()
32
+ return response
33
+
34
+
35
+ class RegoloClient:
36
+ def __init__(self,
37
+ model: Optional[str] = regolo.default_model,
38
+ api_key: Optional[str] = regolo.default_key,
39
+ alternative_url: Optional[str] = None,
40
+ pre_existent_conversation: Optional[Conversation] = None,
41
+ pre_existent_client: httpx.Client = None) -> None:
42
+ """
43
+ Initialize the client for regolo.ai HTTP API.
44
+
45
+ :param model: The regolo.ai model to use. (Defaults to regolo.default_model)
46
+ :param api_key: The API key for regolo.ai. (Defaults to regolo.default_key)
47
+ :param alternative_url: Base URL of the regolo HTTP server. (Optional)
48
+ :param pre_existent_conversation: An existing conversation instance to continue chatting with. (Optional)
49
+ :param pre_existent_client: An existing httpx.Client instance to use. (Optional)
50
+ """
51
+ self.base_url = REGOLO_URL if alternative_url is None else alternative_url
52
+ client = httpx.Client(base_url=self.base_url) if pre_existent_client is None else pre_existent_client
53
+ self.instance = RegoloInstance(model=regolo.default_model if model is None else model,
54
+ api_key=regolo.default_key if api_key is None else api_key,
55
+ previous_conversations=pre_existent_conversation, client=client)
56
+
57
+ @classmethod
58
+ def from_instance(cls, instance: RegoloInstance, alternative_url: Optional[str] = None) -> "RegoloClient":
59
+ """Creates RegoloClient from instance."""
60
+ return cls(api_key=instance.api_key, model=instance.model, alternative_url=alternative_url,
61
+ pre_existent_client=instance.client, pre_existent_conversation=instance.conversation)
62
+
63
+ def change_model(self, model: str) -> None:
64
+ """Change model used in this instance of regolo_client"""
65
+ try:
66
+ self.instance.change_model(new_model=model)
67
+ except Exception as e:
68
+ print(e)
69
+
70
+ @staticmethod
71
+ def get_available_models() -> List[str]:
72
+ """Gets all available models on regolo.ai."""
73
+ return ModelsHandler.get_models()
74
+
75
+ @staticmethod
76
+ def process_vllm_stream(tokens):
77
+ output_text = ""
78
+ for token in tokens:
79
+ if token.startswith("▁"): # SentencePiece-style tokenization
80
+ output_text += " " + token[1:]
81
+ else:
82
+ output_text += token # Continuation of the previous word
83
+ return output_text.strip()
84
+
85
+ @staticmethod
86
+ def create_stream_generator(client: httpx.Client,
87
+ base_url: str,
88
+ payload: dict,
89
+ headers: dict,
90
+ full_output: bool,
91
+ search_url: str,
92
+ output_handler: Callable[[Dict], Any]) -> GeneratorType:
93
+ """
94
+ Yields generators for streams from regolo.ai.
95
+
96
+ :param client: The httpx.Client instance to use.
97
+ :param base_url: Base URL of the regolo HTTP server.
98
+ :param payload: The request payload to send.
99
+ :param headers: The request headers.
100
+ :param full_output: Whether to return the full response.
101
+ :param search_url: The URL for the search request.
102
+ :param output_handler: A function that processes responses if full_output=False.
103
+ :return: A generator that yields streamed responses from regolo.ai.
104
+ """
105
+ with client.stream("POST", f"{base_url}{search_url}", json=payload, headers=headers) as response:
106
+ if response.status_code != 200:
107
+ raise Exception(f"Error: Received unexpected status code {response.status_code}")
108
+
109
+ # Iterate over complete lines instead of raw bytes
110
+ for line in response.iter_lines():
111
+ if not line:
112
+ continue
113
+
114
+ # Decode if necessary and remove the "data:" prefix if present
115
+ decoded_line = line.decode("utf-8") if isinstance(line, bytes) else line
116
+ decoded_line = decoded_line.strip()
117
+ if decoded_line == "data: [DONE]":
118
+ break
119
+ if decoded_line.startswith("data:"):
120
+ decoded_line = decoded_line[len("data:"):].strip()
121
+
122
+ try:
123
+ # Repair and parse the JSON chunk
124
+ data = json.loads(json_repair.repair_json(decoded_line))
125
+ except (Exception,):
126
+ continue
127
+
128
+ if full_output:
129
+ yield data
130
+ else:
131
+ # Handle both dict and list responses uniformly
132
+ yield output_handler(data)
133
+
134
+ # Completions
135
+
136
+ @staticmethod
137
+ def static_completions(prompt: str,
138
+ model: Optional[str] = None,
139
+ api_key: Optional[str] = None,
140
+ stream: bool = False,
141
+ max_tokens: int = 200,
142
+ temperature: Optional[float] = 0.5,
143
+ top_p: Optional[float] = None,
144
+ top_k: Optional[int] = None,
145
+ client: Optional[httpx.Client] = None,
146
+ base_url: str = REGOLO_URL,
147
+ full_output: bool = False) -> GeneratorType:
148
+ """Will return generators for stream=True and values for stream=False
149
+ Send a prompt to regolo server and get the generated response.
150
+
151
+ :param prompt: The input prompt to the LLM.
152
+ :param model: The regolo.ai model to use. (Optional)
153
+ :param api_key: The API key for regolo.ai. (Optional)
154
+ :param stream: Whether to stream the prompt from regolo.ai. (Defaults to False)
155
+ :param max_tokens: Maximum number of tokens to generate. (Defaults to 200)
156
+ :param temperature: Sampling temperature for randomness. (Optional)
157
+ :param top_p: Nucleus sampling parameter. (Optional)
158
+ :param top_k: Top-k sampling parameter. (Optional)
159
+ :param client: httpx client to use. (Optional)
160
+ :param base_url: Base URL of the regolo HTTP server. (Defaults to REGOLO_URL)
161
+ :param full_output: Whether to return the full response. (Defaults to False)
162
+
163
+ :return: Response from the vLLM server.
164
+ """
165
+
166
+ def handle_search_text_completions(data: dict) -> str:
167
+ return data.get("choices", [{}])[0].get("text")
168
+
169
+ if api_key is None:
170
+ api_key = regolo.default_key
171
+
172
+ api_key = KeysHandler.check_key(api_key)
173
+
174
+ if model is None:
175
+ model = regolo.default_model
176
+
177
+ ModelsHandler.check_model(model)
178
+
179
+ if client is None:
180
+ client = httpx.Client()
181
+ payload = {
182
+ "prompt": prompt,
183
+ "stream": stream,
184
+ "model": model,
185
+ "max_tokens": max_tokens,
186
+ "temperature": temperature,
187
+ "top_p": top_p,
188
+ "top_k": top_k
189
+ }
190
+
191
+ # Filter out parameters that are None
192
+ payload = {k: v for k, v in payload.items() if v is not None}
193
+ headers = {"Authorization": api_key}
194
+ if stream:
195
+ return RegoloClient.create_stream_generator(client=client, base_url=base_url, payload=payload,
196
+ headers=headers,
197
+ full_output=full_output, search_url=COMPLETIONS_URL_PATH,
198
+ output_handler=handle_search_text_completions)
199
+ else:
200
+ response = safe_post(client=client, url_to_query=f"{base_url}{COMPLETIONS_URL_PATH}",
201
+ json_to_query=payload, headers_to_query=headers)
202
+ if full_output:
203
+ return response.json()
204
+ else:
205
+ return response.json()["choices"][0]["text"]
206
+
207
+ def completions(self,
208
+ prompt: str,
209
+ stream: Optional[bool] = False,
210
+ max_tokens: int = 200,
211
+ temperature: Optional[float] = None,
212
+ top_p: Optional[float] = None,
213
+ top_k: Optional[int] = None,
214
+ full_output: bool = False) -> Dict[str, Any] | GeneratorType:
215
+ """Performs requests to completions endpoint from RegoloClient instance."""
216
+ response = self.static_completions(prompt=prompt,
217
+ model=self.instance.get_model(),
218
+ api_key=self.instance.get_api_key(),
219
+ stream=stream,
220
+ max_tokens=max_tokens,
221
+ temperature=temperature,
222
+ top_p=top_p,
223
+ top_k=top_k,
224
+ client=self.instance.client,
225
+ base_url=self.base_url,
226
+ full_output=full_output)
227
+
228
+ return response
229
+
230
+ # Chat completions
231
+
232
+ @staticmethod
233
+ def static_chat_completions(messages: Conversation | List[Dict[str, str]],
234
+ model: Optional[str] = None,
235
+ api_key: Optional[str] = None,
236
+ stream: bool = False,
237
+ max_tokens: int = 200,
238
+ temperature: Optional[float] = None,
239
+ top_p: Optional[float] = None,
240
+ top_k: Optional[int] = None,
241
+ client: Optional[httpx.Client] = None,
242
+ base_url: str = REGOLO_URL,
243
+ full_output: bool = False
244
+ ) -> GeneratorType | tuple[Role, Content] | dict:
245
+ """
246
+ Sends a series of chat messages to the vLLM server and gets the response.
247
+
248
+ :param messages: A list of messages in the format [{"role": "user"|"assistant", "content": "message"}].
249
+ :param model: The regolo.ai model to use. (Optional)
250
+ :param api_key: The API key for regolo.ai. (Optional)
251
+ :param stream: Whether to stream the prompt from regolo.ai. (Defaults to False)
252
+ :param max_tokens: Maximum number of tokens to generate. (Defaults to 200)
253
+ :param temperature: Sampling temperature for randomness. (Optional)
254
+ :param top_p: Nucleus sampling parameter. (Optional)
255
+ :param top_k: Top-k sampling parameter. (Optional)
256
+ :param client: httpx client to use. (Optional)
257
+ :param base_url: Base URL of the regolo HTTP server. (Defaults to REGOLO_URL)
258
+ :param full_output: Whether to return full response. (Defaults to False)
259
+
260
+ :return for stream=true, full_output=False: Generator, which yields dicts with the responses from regolo.ai.
261
+ :return for stream=True, full_output=True: Generator, which yields tuples of role, content of response.
262
+ :return for stream=False, full_output=False: String with response from regolo.ai.
263
+ :return for stream=False, full_output=True: Tuple which consists of role and content of response.
264
+ """
265
+
266
+ def handle_search_text_chat_completions(data: dict) -> tuple[Role, Content]:
267
+ """Internal method, describes how RegoloClient.create_stream_generator should handle output from chat_completions."""
268
+ if isinstance(data, dict):
269
+ delta = data.get("choices", [{}])[0].get("delta", {})
270
+ out_role = delta.get("role", "")
271
+ out_content = delta.get("content", "")
272
+ return out_role, out_content
273
+ elif isinstance(data, list):
274
+ for element in data:
275
+ delta = element.get("choices", [{}])[0].get("delta", {})
276
+ out_role = delta.get("role", "")
277
+ out_content = delta.get("content", "")
278
+ return out_role, out_content
279
+
280
+ if api_key is None:
281
+ api_key = regolo.default_key
282
+
283
+ api_key = KeysHandler.check_key(api_key)
284
+
285
+ if model is None:
286
+ model = regolo.default_model
287
+
288
+ ModelsHandler.check_model(model)
289
+
290
+ if type(messages) == Conversation:
291
+ messages = messages.get_lines()
292
+ api_key = KeysHandler.check_key(api_key)
293
+
294
+ if client is None:
295
+ client = httpx.Client()
296
+
297
+ payload = {
298
+ "model": model,
299
+ "stream": stream,
300
+ "messages": messages,
301
+ "max_tokens": max_tokens,
302
+ "temperature": temperature,
303
+ "top_p": top_p,
304
+ "top_k": top_k
305
+ }
306
+
307
+ # Filter out parameters that are None
308
+ payload = {k: v for k, v in payload.items() if v is not None}
309
+ headers = {"Authorization": api_key}
310
+
311
+ if stream:
312
+ return RegoloClient.create_stream_generator(client=client, base_url=base_url, payload=payload,
313
+ headers=headers,
314
+ full_output=full_output, search_url=CHAT_COMPLETIONS_URL_PATH,
315
+ output_handler=handle_search_text_chat_completions)
316
+ else:
317
+ response = safe_post(client=client, url_to_query=f"{base_url}{CHAT_COMPLETIONS_URL_PATH}",
318
+ json_to_query=payload, headers_to_query=headers).json()
319
+ if full_output:
320
+ return response
321
+ else:
322
+ role = response["choices"][0]["message"]["role"]
323
+ content = response["choices"][0]["message"]["content"]
324
+ return role, content
325
+
326
+ def add_prompt_to_chat(self, prompt: str, role: str):
327
+ """
328
+ Adds a prompt to the chat as the role specified
329
+
330
+ :param prompt: The prompt to add.
331
+ :param role: The role of the prompt to add.
332
+
333
+ :example:
334
+ client = RegoloClient()
335
+ client.add_prompt_to_chat(prompt="how are you?", role="user")
336
+ print(client.run_chat())
337
+ """
338
+
339
+ self.instance.add_prompt_as_role(prompt=prompt, role=role)
340
+
341
+ def clear_conversations(self) -> None:
342
+ """clear all prompts to start new conversations."""
343
+ self.instance.clear_conversation()
344
+
345
+ def run_chat(self,
346
+ user_prompt: Optional[str] = None,
347
+ stream: bool = False,
348
+ max_tokens: int = 200,
349
+ temperature: Optional[float] = None,
350
+ top_p: Optional[float] = None,
351
+ top_k: Optional[int] = None,
352
+ full_output: bool = False) -> GeneratorType | tuple[Role, Content]:
353
+ """
354
+ Runs chat endpoint from RegoloClient instance (self.conversation contains the role-prompts dicts).
355
+
356
+ :param user_prompt: Optional prompt to add to conversation before generating response from regolo.ai.
357
+ :param stream: Whether to stream the prompt from regolo.ai.
358
+ :param max_tokens: Maximum number of tokens to generate. (Defaults to 200)
359
+ :param temperature: Sampling temperature for randomness.
360
+ :param top_p: Nucleus sampling parameter.
361
+ :param top_k: Top-k sampling parameter.
362
+ :param full_output: Whether to return full response. (Defaults to False)
363
+ :return for stream=true, full_output=False: Generator, which yields dicts with the responses from regolo.ai.
364
+ :return for stream=True, full_output=True: Generator, which yields tuples of role, content of response.
365
+ :return for stream=False, full_output=False: String with response from regolo.ai.
366
+ :return for stream=False, full_output=True: Tuple which consists of role and content of response.
367
+ """
368
+
369
+ if user_prompt is not None:
370
+ self.instance.add_prompt_as_role(prompt=user_prompt, role="user")
371
+
372
+ response = self.static_chat_completions(messages=self.instance.get_conversation(),
373
+ model=self.instance.get_model(),
374
+ stream=stream,
375
+ api_key=self.instance.get_api_key(),
376
+ max_tokens=max_tokens,
377
+ temperature=temperature,
378
+ top_p=top_p,
379
+ top_k=top_k,
380
+ client=self.instance.get_client(),
381
+ base_url=self.base_url,
382
+ full_output=full_output)
383
+
384
+ if stream is True:
385
+ return response
386
+ else:
387
+ if full_output:
388
+ responseRole = response["choices"][0]["message"]["role"]
389
+ responseText = response["choices"][0]["message"]["content"]
390
+ else:
391
+ responseRole = response[0]
392
+ responseText = response[1]
393
+
394
+ self.instance.add_line(ConversationLine(role=responseRole, content=responseText))
395
+
396
+ return response
File without changes
@@ -0,0 +1,51 @@
1
+ from typing import Optional, List, Dict
2
+
3
+ import httpx
4
+
5
+ from regolo.instance.structures.conversation_model import Conversation, ConversationLine
6
+
7
+ from regolo.models.models import ModelsHandler
8
+ from regolo.keys.keys import KeysHandler
9
+
10
+
11
+ class RegoloInstance:
12
+ def __init__(self, model: str, api_key: str, client: Optional[httpx.Client],
13
+ previous_conversations: Optional[Conversation] = None) -> None:
14
+ self.conversation: Conversation = Conversation(
15
+ lines=[]) if previous_conversations is None else previous_conversations
16
+ self.client: httpx.Client = httpx.Client() if client is None else client
17
+ self.api_key = KeysHandler.check_key(api_key)
18
+ self.model: str = model
19
+
20
+ def get_client(self) -> httpx.Client:
21
+ return self.client
22
+
23
+ def get_api_key(self) -> str:
24
+ return self.api_key
25
+
26
+ def get_model(self) -> str:
27
+ return self.model
28
+
29
+ def change_model(self, new_model: str) -> None:
30
+ self.model = ModelsHandler.check_model(new_model)
31
+
32
+ def get_conversation(self) -> List[Dict[str, str]]:
33
+ """Get conversation as list of dicts"""
34
+ return self.conversation.get_lines()
35
+
36
+ def add_line(self, conversation: ConversationLine) -> None:
37
+ """Adds a line to the conversation"""
38
+ self.conversation.lines.append(conversation)
39
+
40
+ def overwrite_conversation(self, conversation: Conversation) -> None:
41
+ """Overwrites the conversation with the given one"""
42
+ self.conversation = conversation
43
+
44
+ def clear_conversation(self) -> None:
45
+ """Clears the conversation to start a new chat from scratch"""
46
+ self.conversation = Conversation(lines=[])
47
+
48
+ def add_prompt_as_role(self, prompt: str, role: str) -> None:
49
+ """Adds a prompt as given role to the conversation.
50
+ Normally roles are "user" and "assistant", but they can vary based on model."""
51
+ self.conversation.lines.append(ConversationLine(role=role, content=prompt))
@@ -0,0 +1,28 @@
1
+ import json
2
+
3
+ from pydantic import BaseModel
4
+ from typing import List, Dict
5
+
6
+ from pydantic import StrictStr
7
+
8
+
9
+ class ConversationLine(BaseModel):
10
+ role: StrictStr
11
+ content: StrictStr
12
+
13
+
14
+ class Conversation(BaseModel):
15
+ lines: List[ConversationLine]
16
+
17
+ def __init__(self, lines: List[ConversationLine]):
18
+ super().__init__(lines=lines)
19
+
20
+ def to_json(self):
21
+ return json.dumps(self.model_dump(), sort_keys=True)
22
+
23
+ def get_lines(self) -> List[Dict[str, str]]:
24
+ return json.loads((self.to_json()))["lines"]
25
+
26
+ def print_conversation(self):
27
+ for line in self.get_lines():
28
+ print(f"{line["role"]}: {line["content"]}\n")
File without changes
@@ -0,0 +1,12 @@
1
+ class KeysHandler:
2
+ @staticmethod
3
+ def fix_key(api_key: str) -> str:
4
+ if not api_key.startswith("Bearer "):
5
+ return "Bearer " + api_key
6
+ return api_key
7
+
8
+ @staticmethod
9
+ def check_key(api_key: str) -> str:
10
+ if api_key is None:
11
+ raise RuntimeError("API key is required")
12
+ return KeysHandler.fix_key(api_key)
File without changes
@@ -0,0 +1,19 @@
1
+ import json
2
+
3
+ import httpx
4
+
5
+
6
+ class ModelsHandler:
7
+ @staticmethod
8
+ def get_models() -> list[str]:
9
+ models = json.loads(httpx.get("https://regolo.ai/models.json").text)["models"]
10
+ return [model["id"] for model in models]
11
+
12
+ @staticmethod
13
+ def check_model(model: str) -> str:
14
+ if model is None:
15
+ raise RuntimeError("Model is required")
16
+ elif model not in ModelsHandler.get_models():
17
+ raise RuntimeError("Model not found")
18
+ # TODO: fix model request if requested model is understandable
19
+ return model
@@ -0,0 +1,180 @@
1
+ Metadata-Version: 2.2
2
+ Name: regolo
3
+ Version: 1.0.0
4
+ Summary: Simple client to interact with regolo.ai
5
+ Author-email: "regolo.ai" <dev@regolo.ai>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 regolo.ai
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/regolo-ai/python-client
29
+ Keywords: regolo,llm,chat
30
+ Classifier: License :: OSI Approved :: MIT License
31
+ Classifier: Programming Language :: Python
32
+ Classifier: Programming Language :: Python :: 3
33
+ Requires-Python: >=3.12
34
+ Description-Content-Type: text/markdown
35
+ License-File: LICENSE
36
+ Requires-Dist: httpx~=0.28.1
37
+ Requires-Dist: pydantic~=2.10.6
38
+ Requires-Dist: json_repair~=0.35.0
39
+
40
+ # **Regolo.ai Python Client**
41
+
42
+ A simple Python client for interacting for **Regolo.ai's** LLM-based API.
43
+
44
+ ## **Installation**
45
+ Ensure you have the `regolo` module installed. If not, install it using:
46
+
47
+ ```bash
48
+ pip install regolo
49
+ ```
50
+
51
+ # **Basic Usage**
52
+
53
+ ## **1. Import the regolo module**
54
+
55
+ ```python
56
+ import regolo
57
+ ```
58
+ ## **2. Set Up Default API Key and Model**
59
+
60
+ To avoid manually passing the API key and model in every request, you can set them globally:
61
+
62
+ ```python
63
+ regolo.default_key = "<EXAMPLE_KEY>"
64
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
65
+ ```
66
+
67
+ This ensures that all `RegoloClient` instances and static functions will
68
+ use the specified API key and model.
69
+
70
+ Still, you can create run methods by passing model and key directly.
71
+
72
+ ## **3. Perform a basic request**
73
+
74
+ ### Completion:
75
+ ```python
76
+ print(regolo.static_completions(prompt="Tell me something about Rome."))
77
+ ```
78
+
79
+ ### Chat_completion
80
+ ```python
81
+ print(regolo.static_chat_completions(messages=[{"role": "user", "content": "Tell me something about rome"}]))
82
+ ```
83
+
84
+ ---
85
+
86
+ # **Other usages**
87
+
88
+ ## **Handling streams**
89
+
90
+
91
+ **With full output:**
92
+
93
+ ```python
94
+ import regolo
95
+ regolo.default_key = "<EXAMPLE_KEY>"
96
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
97
+
98
+ # Completions
99
+
100
+ client = regolo.RegoloClient()
101
+ response = client.completions("Tell me about Rome in a concise manner", full_output=True, stream=True)
102
+
103
+ while True:
104
+ try:
105
+ print(next(response))
106
+ except StopIteration:
107
+ break
108
+
109
+ # Chat completions
110
+
111
+ client = regolo.RegoloClient()
112
+ response = client.run_chat(user_prompt="Tell me about Rome in a concise manner", full_output=True, stream=True)
113
+
114
+
115
+ while True:
116
+ try:
117
+ print(next(response))
118
+ except StopIteration:
119
+ break
120
+ ```
121
+
122
+ **Without full output:**
123
+
124
+ ```python
125
+ import regolo
126
+ regolo.default_key = "<EXAMPLE_KEY>"
127
+ regolo.default_model = "meta-llama/Llama-3.3-70B-Instruct"
128
+
129
+ # Completions
130
+
131
+ client = regolo.RegoloClient()
132
+ response = client.completions("Tell me about Rome in a concise manner", full_output=True, stream=True)
133
+
134
+ while True:
135
+ try:
136
+ print(next(response), end='', flush=True)
137
+ except StopIteration:
138
+ break
139
+
140
+ # Chat completions
141
+
142
+ client = regolo.RegoloClient()
143
+ response = client.run_chat(user_prompt="Tell me about Rome in a concise manner", full_output=True, stream=True)
144
+
145
+ while True:
146
+ try:
147
+ res = next(response)
148
+ if res[0]:
149
+ print(res[0] + ":")
150
+ print(res[1], end="", flush=True)
151
+ except StopIteration:
152
+ break
153
+ ```
154
+
155
+ ## **Handling chat through add_prompt_to_chat()**
156
+
157
+ ```python
158
+ import regolo
159
+
160
+ client = regolo.RegoloClient()
161
+
162
+ # Make a request
163
+
164
+ client.add_prompt_to_chat(role="user", prompt="Tell me about rome!")
165
+
166
+ print(client.run_chat())
167
+
168
+ # Continue the conversation
169
+
170
+ client.add_prompt_to_chat(role="user", prompt="Tell me something more about it!")
171
+
172
+ print(client.run_chat())
173
+
174
+ # You can print the whole conversation if needed
175
+
176
+ print(print(client.instance.get_conversation()))
177
+ ```
178
+
179
+ It is to consider that using the user_prompt parameter in run_chat() is equivalent to adding a prompt with role=user
180
+ through add_prompt_to_chat().
@@ -0,0 +1,19 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/regolo/__init__.py
5
+ src/regolo.egg-info/PKG-INFO
6
+ src/regolo.egg-info/SOURCES.txt
7
+ src/regolo.egg-info/dependency_links.txt
8
+ src/regolo.egg-info/requires.txt
9
+ src/regolo.egg-info/top_level.txt
10
+ src/regolo/client/__init__.py
11
+ src/regolo/client/regolo_client.py
12
+ src/regolo/instance/__init__.py
13
+ src/regolo/instance/regolo_instance.py
14
+ src/regolo/instance/structures/__init__.py
15
+ src/regolo/instance/structures/conversation_model.py
16
+ src/regolo/keys/__init__.py
17
+ src/regolo/keys/keys.py
18
+ src/regolo/models/__init__.py
19
+ src/regolo/models/models.py
@@ -0,0 +1,3 @@
1
+ httpx~=0.28.1
2
+ pydantic~=2.10.6
3
+ json_repair~=0.35.0
@@ -0,0 +1 @@
1
+ regolo