aiverify-moonshot 0.4.0__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.
- aiverify_moonshot-0.4.0.dist-info/METADATA +249 -0
- aiverify_moonshot-0.4.0.dist-info/RECORD +163 -0
- aiverify_moonshot-0.4.0.dist-info/WHEEL +4 -0
- aiverify_moonshot-0.4.0.dist-info/licenses/AUTHORS.md +5 -0
- aiverify_moonshot-0.4.0.dist-info/licenses/LICENSE.md +201 -0
- aiverify_moonshot-0.4.0.dist-info/licenses/NOTICES.md +3340 -0
- moonshot/__init__.py +0 -0
- moonshot/__main__.py +198 -0
- moonshot/api.py +155 -0
- moonshot/integrations/__init__.py +0 -0
- moonshot/integrations/cli/__init__.py +0 -0
- moonshot/integrations/cli/__main__.py +25 -0
- moonshot/integrations/cli/active_session_cfg.py +1 -0
- moonshot/integrations/cli/benchmark/__init__.py +0 -0
- moonshot/integrations/cli/benchmark/benchmark.py +186 -0
- moonshot/integrations/cli/benchmark/cookbook.py +545 -0
- moonshot/integrations/cli/benchmark/datasets.py +164 -0
- moonshot/integrations/cli/benchmark/metrics.py +141 -0
- moonshot/integrations/cli/benchmark/recipe.py +598 -0
- moonshot/integrations/cli/benchmark/result.py +216 -0
- moonshot/integrations/cli/benchmark/run.py +140 -0
- moonshot/integrations/cli/benchmark/runner.py +174 -0
- moonshot/integrations/cli/cli.py +64 -0
- moonshot/integrations/cli/common/__init__.py +0 -0
- moonshot/integrations/cli/common/common.py +72 -0
- moonshot/integrations/cli/common/connectors.py +325 -0
- moonshot/integrations/cli/common/display_helper.py +42 -0
- moonshot/integrations/cli/common/prompt_template.py +94 -0
- moonshot/integrations/cli/initialisation/__init__.py +0 -0
- moonshot/integrations/cli/initialisation/initialisation.py +14 -0
- moonshot/integrations/cli/redteam/__init__.py +0 -0
- moonshot/integrations/cli/redteam/attack_module.py +70 -0
- moonshot/integrations/cli/redteam/context_strategy.py +147 -0
- moonshot/integrations/cli/redteam/prompt_template.py +67 -0
- moonshot/integrations/cli/redteam/redteam.py +90 -0
- moonshot/integrations/cli/redteam/session.py +467 -0
- moonshot/integrations/web_api/.env.dev +7 -0
- moonshot/integrations/web_api/__init__.py +0 -0
- moonshot/integrations/web_api/__main__.py +56 -0
- moonshot/integrations/web_api/app.py +125 -0
- moonshot/integrations/web_api/container.py +146 -0
- moonshot/integrations/web_api/log/.gitkeep +0 -0
- moonshot/integrations/web_api/logging_conf.py +114 -0
- moonshot/integrations/web_api/routes/__init__.py +0 -0
- moonshot/integrations/web_api/routes/attack_modules.py +66 -0
- moonshot/integrations/web_api/routes/benchmark.py +116 -0
- moonshot/integrations/web_api/routes/benchmark_result.py +175 -0
- moonshot/integrations/web_api/routes/context_strategy.py +129 -0
- moonshot/integrations/web_api/routes/cookbook.py +225 -0
- moonshot/integrations/web_api/routes/dataset.py +120 -0
- moonshot/integrations/web_api/routes/endpoint.py +282 -0
- moonshot/integrations/web_api/routes/metric.py +78 -0
- moonshot/integrations/web_api/routes/prompt_template.py +128 -0
- moonshot/integrations/web_api/routes/recipe.py +219 -0
- moonshot/integrations/web_api/routes/redteam.py +609 -0
- moonshot/integrations/web_api/routes/runner.py +239 -0
- moonshot/integrations/web_api/schemas/__init__.py +0 -0
- moonshot/integrations/web_api/schemas/benchmark_runner_dto.py +13 -0
- moonshot/integrations/web_api/schemas/cookbook_create_dto.py +19 -0
- moonshot/integrations/web_api/schemas/cookbook_response_model.py +9 -0
- moonshot/integrations/web_api/schemas/dataset_response_dto.py +9 -0
- moonshot/integrations/web_api/schemas/endpoint_create_dto.py +21 -0
- moonshot/integrations/web_api/schemas/endpoint_response_model.py +11 -0
- moonshot/integrations/web_api/schemas/prompt_response_model.py +14 -0
- moonshot/integrations/web_api/schemas/prompt_template_response_model.py +10 -0
- moonshot/integrations/web_api/schemas/recipe_create_dto.py +32 -0
- moonshot/integrations/web_api/schemas/recipe_response_model.py +7 -0
- moonshot/integrations/web_api/schemas/session_create_dto.py +16 -0
- moonshot/integrations/web_api/schemas/session_prompt_dto.py +7 -0
- moonshot/integrations/web_api/schemas/session_response_model.py +38 -0
- moonshot/integrations/web_api/services/__init__.py +0 -0
- moonshot/integrations/web_api/services/attack_module_service.py +34 -0
- moonshot/integrations/web_api/services/auto_red_team_test_manager.py +86 -0
- moonshot/integrations/web_api/services/auto_red_team_test_state.py +57 -0
- moonshot/integrations/web_api/services/base_service.py +8 -0
- moonshot/integrations/web_api/services/benchmark_result_service.py +25 -0
- moonshot/integrations/web_api/services/benchmark_test_manager.py +106 -0
- moonshot/integrations/web_api/services/benchmark_test_state.py +56 -0
- moonshot/integrations/web_api/services/benchmarking_service.py +31 -0
- moonshot/integrations/web_api/services/context_strategy_service.py +22 -0
- moonshot/integrations/web_api/services/cookbook_service.py +194 -0
- moonshot/integrations/web_api/services/dataset_service.py +20 -0
- moonshot/integrations/web_api/services/endpoint_service.py +65 -0
- moonshot/integrations/web_api/services/metric_service.py +14 -0
- moonshot/integrations/web_api/services/prompt_template_service.py +39 -0
- moonshot/integrations/web_api/services/recipe_service.py +155 -0
- moonshot/integrations/web_api/services/runner_service.py +147 -0
- moonshot/integrations/web_api/services/session_service.py +350 -0
- moonshot/integrations/web_api/services/utils/exceptions_handler.py +41 -0
- moonshot/integrations/web_api/services/utils/results_formatter.py +47 -0
- moonshot/integrations/web_api/status_updater/interface/benchmark_progress_callback.py +14 -0
- moonshot/integrations/web_api/status_updater/interface/redteam_progress_callback.py +14 -0
- moonshot/integrations/web_api/status_updater/moonshot_ui_webhook.py +72 -0
- moonshot/integrations/web_api/types/types.py +99 -0
- moonshot/src/__init__.py +0 -0
- moonshot/src/api/__init__.py +0 -0
- moonshot/src/api/api_connector.py +58 -0
- moonshot/src/api/api_connector_endpoint.py +162 -0
- moonshot/src/api/api_context_strategy.py +57 -0
- moonshot/src/api/api_cookbook.py +160 -0
- moonshot/src/api/api_dataset.py +46 -0
- moonshot/src/api/api_environment_variables.py +17 -0
- moonshot/src/api/api_metrics.py +51 -0
- moonshot/src/api/api_prompt_template.py +43 -0
- moonshot/src/api/api_recipe.py +182 -0
- moonshot/src/api/api_red_teaming.py +59 -0
- moonshot/src/api/api_result.py +84 -0
- moonshot/src/api/api_run.py +74 -0
- moonshot/src/api/api_runner.py +132 -0
- moonshot/src/api/api_session.py +290 -0
- moonshot/src/configs/__init__.py +0 -0
- moonshot/src/configs/env_variables.py +187 -0
- moonshot/src/connectors/__init__.py +0 -0
- moonshot/src/connectors/connector.py +327 -0
- moonshot/src/connectors/connector_prompt_arguments.py +17 -0
- moonshot/src/connectors_endpoints/__init__.py +0 -0
- moonshot/src/connectors_endpoints/connector_endpoint.py +211 -0
- moonshot/src/connectors_endpoints/connector_endpoint_arguments.py +54 -0
- moonshot/src/cookbooks/__init__.py +0 -0
- moonshot/src/cookbooks/cookbook.py +225 -0
- moonshot/src/cookbooks/cookbook_arguments.py +34 -0
- moonshot/src/datasets/__init__.py +0 -0
- moonshot/src/datasets/dataset.py +255 -0
- moonshot/src/datasets/dataset_arguments.py +50 -0
- moonshot/src/metrics/__init__.py +0 -0
- moonshot/src/metrics/metric.py +192 -0
- moonshot/src/metrics/metric_interface.py +95 -0
- moonshot/src/prompt_templates/__init__.py +0 -0
- moonshot/src/prompt_templates/prompt_template.py +103 -0
- moonshot/src/recipes/__init__.py +0 -0
- moonshot/src/recipes/recipe.py +340 -0
- moonshot/src/recipes/recipe_arguments.py +111 -0
- moonshot/src/redteaming/__init__.py +0 -0
- moonshot/src/redteaming/attack/__init__.py +0 -0
- moonshot/src/redteaming/attack/attack_module.py +618 -0
- moonshot/src/redteaming/attack/attack_module_arguments.py +44 -0
- moonshot/src/redteaming/attack/context_strategy.py +131 -0
- moonshot/src/redteaming/context_strategy/__init__.py +0 -0
- moonshot/src/redteaming/context_strategy/context_strategy_interface.py +46 -0
- moonshot/src/redteaming/session/__init__.py +0 -0
- moonshot/src/redteaming/session/chat.py +209 -0
- moonshot/src/redteaming/session/red_teaming_progress.py +128 -0
- moonshot/src/redteaming/session/red_teaming_type.py +6 -0
- moonshot/src/redteaming/session/session.py +775 -0
- moonshot/src/results/__init__.py +0 -0
- moonshot/src/results/result.py +119 -0
- moonshot/src/results/result_arguments.py +44 -0
- moonshot/src/runners/__init__.py +0 -0
- moonshot/src/runners/runner.py +476 -0
- moonshot/src/runners/runner_arguments.py +46 -0
- moonshot/src/runners/runner_type.py +6 -0
- moonshot/src/runs/__init__.py +0 -0
- moonshot/src/runs/run.py +344 -0
- moonshot/src/runs/run_arguments.py +162 -0
- moonshot/src/runs/run_progress.py +145 -0
- moonshot/src/runs/run_status.py +10 -0
- moonshot/src/storage/__init__.py +0 -0
- moonshot/src/storage/db_interface.py +128 -0
- moonshot/src/storage/io_interface.py +31 -0
- moonshot/src/storage/storage.py +525 -0
- moonshot/src/utils/__init__.py +0 -0
- moonshot/src/utils/import_modules.py +96 -0
- moonshot/src/utils/timeit.py +25 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import time
|
|
5
|
+
from abc import abstractmethod
|
|
6
|
+
from asyncio import sleep
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Callable
|
|
10
|
+
|
|
11
|
+
from moonshot.src.configs.env_variables import EnvVariables
|
|
12
|
+
from moonshot.src.connectors.connector_prompt_arguments import ConnectorPromptArguments
|
|
13
|
+
from moonshot.src.connectors_endpoints.connector_endpoint_arguments import (
|
|
14
|
+
ConnectorEndpointArguments,
|
|
15
|
+
)
|
|
16
|
+
from moonshot.src.storage.storage import Storage
|
|
17
|
+
from moonshot.src.utils.import_modules import get_instance
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def perform_retry(func):
|
|
21
|
+
"""
|
|
22
|
+
A decorator to perform retries on a function.
|
|
23
|
+
|
|
24
|
+
This decorator wraps a function to enable retrying the function call
|
|
25
|
+
if it fails. The number of retries and the delay between retries
|
|
26
|
+
are determined by the `retries_times` and `allow_retries`
|
|
27
|
+
attributes of the class instance.
|
|
28
|
+
|
|
29
|
+
Parameters:
|
|
30
|
+
- func (Callable): The function to be wrapped and retried.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
- Callable: A wrapper function that includes retry logic.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
async def wrapper(self, *args, **kwargs):
|
|
37
|
+
if self.allow_retries:
|
|
38
|
+
retry_count = 0
|
|
39
|
+
base_delay = 1
|
|
40
|
+
while retry_count <= self.retries_times:
|
|
41
|
+
# Perform the request
|
|
42
|
+
try:
|
|
43
|
+
return await func(self, *args, **kwargs)
|
|
44
|
+
except Exception as exc:
|
|
45
|
+
print(f"Operation failed. {str(exc)} - Retrying...")
|
|
46
|
+
|
|
47
|
+
# Perform retry
|
|
48
|
+
retry_count += 1
|
|
49
|
+
if retry_count <= self.retries_times:
|
|
50
|
+
delay = base_delay * (2**retry_count)
|
|
51
|
+
print(f"Attempt {retry_count}, Retrying in {delay} seconds...")
|
|
52
|
+
await sleep(delay)
|
|
53
|
+
# Raise an exception
|
|
54
|
+
raise ConnectionError("Failed to get response.")
|
|
55
|
+
|
|
56
|
+
return wrapper
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class Connector:
|
|
60
|
+
def __init__(self, ep_args: ConnectorEndpointArguments) -> None:
|
|
61
|
+
self.id = ep_args.id
|
|
62
|
+
|
|
63
|
+
self.endpoint = ep_args.uri
|
|
64
|
+
self.token = ep_args.token
|
|
65
|
+
self.max_concurrency = ep_args.max_concurrency
|
|
66
|
+
self.max_calls_per_second = ep_args.max_calls_per_second
|
|
67
|
+
self.params = ep_args.params
|
|
68
|
+
|
|
69
|
+
# Rate limiting
|
|
70
|
+
self.rate_limiter = ep_args.max_calls_per_second
|
|
71
|
+
# Initialize the token count to the maximum limit
|
|
72
|
+
self.tokens = ep_args.max_calls_per_second
|
|
73
|
+
self.updated_at = time.time()
|
|
74
|
+
self.semaphore = asyncio.Semaphore(ep_args.max_concurrency)
|
|
75
|
+
|
|
76
|
+
# Set Prompts if they exists
|
|
77
|
+
self.pre_prompt = ep_args.params.get("pre_prompt", "")
|
|
78
|
+
self.post_prompt = ep_args.params.get("post_prompt", "")
|
|
79
|
+
self.system_prompt = ep_args.params.get("system_prompt", "")
|
|
80
|
+
|
|
81
|
+
# Connection timeout
|
|
82
|
+
self.timeout = ep_args.params.get("timeout", 600)
|
|
83
|
+
self.allow_retries = ep_args.params.get("allow_retries", True)
|
|
84
|
+
self.retries_times = ep_args.params.get("num_of_retries", 3)
|
|
85
|
+
|
|
86
|
+
# Optional params
|
|
87
|
+
excluded_keys = {
|
|
88
|
+
"allow_retries",
|
|
89
|
+
"timeout",
|
|
90
|
+
"num_of_retries",
|
|
91
|
+
"pre_prompt",
|
|
92
|
+
"post_prompt",
|
|
93
|
+
"system_prompt",
|
|
94
|
+
}
|
|
95
|
+
self.optional_params = {
|
|
96
|
+
k: v for k, v in ep_args.params.items() if k not in excluded_keys
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async def _add_tokens(self) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Replenishes the token bucket based on the elapsed time since the last update.
|
|
102
|
+
|
|
103
|
+
This method calculates the number of tokens to add to the bucket by considering the time that has elapsed
|
|
104
|
+
since the tokens were last replenished. The rate at which tokens are added is determined by the `rate_limiter`
|
|
105
|
+
attribute, which defines the maximum number of tokens that can be added per second. The total number of tokens
|
|
106
|
+
in the bucket will never exceed the rate limit.
|
|
107
|
+
|
|
108
|
+
The method updates the `updated_at` attribute to the current time after tokens are added, ensuring that
|
|
109
|
+
the next token addition will be calculated based on the correct elapsed time.
|
|
110
|
+
|
|
111
|
+
Usage:
|
|
112
|
+
# Assume `self` is an instance of a class with `rate_limiter`, `tokens`, and `updated_at` attributes.
|
|
113
|
+
await self.add_tokens()
|
|
114
|
+
"""
|
|
115
|
+
now = time.time()
|
|
116
|
+
elapsed = now - self.updated_at
|
|
117
|
+
# Add tokens based on elapsed time
|
|
118
|
+
increment = elapsed * self.rate_limiter
|
|
119
|
+
self.tokens = min(self.rate_limiter, self.tokens + increment)
|
|
120
|
+
self.updated_at = now
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def rate_limited(func: Callable) -> Callable:
|
|
124
|
+
"""
|
|
125
|
+
A decorator to enforce rate limiting on an asynchronous function using a token bucket strategy.
|
|
126
|
+
|
|
127
|
+
This decorator ensures that the decorated function adheres to a rate limit specified by the `rate_limiter`
|
|
128
|
+
attribute of the class instance it belongs to. It uses a token bucket mechanism where tokens are added
|
|
129
|
+
to the bucket over time, and each function call consumes a token. If there are no tokens available,
|
|
130
|
+
the function's execution is delayed until the next token is added.
|
|
131
|
+
|
|
132
|
+
The decorator also uses an `asyncio.Semaphore` to control concurrency, allowing multiple instances of the
|
|
133
|
+
function to run in parallel up to a limit, without exceeding the rate limit.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
func (Callable): The asynchronous function to be decorated.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
Callable: The decorated function wrapped with rate limiting logic.
|
|
140
|
+
|
|
141
|
+
Usage:
|
|
142
|
+
@rate_limited
|
|
143
|
+
async def some_async_function(*args, **kwargs):
|
|
144
|
+
# Function implementation
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
@wraps(func)
|
|
148
|
+
async def wrapper(self, *args, **kwargs):
|
|
149
|
+
async with self.semaphore:
|
|
150
|
+
# Wait for token availability
|
|
151
|
+
await self._add_tokens()
|
|
152
|
+
if self.tokens < 1:
|
|
153
|
+
# Calculate the time to wait until the next token is available
|
|
154
|
+
sleep_time = (1 - self.tokens) / self.rate_limiter
|
|
155
|
+
await asyncio.sleep(sleep_time)
|
|
156
|
+
# Re-check for token availability after sleeping
|
|
157
|
+
await self._add_tokens()
|
|
158
|
+
# Consume a token and proceed with the function call
|
|
159
|
+
self.tokens -= 1
|
|
160
|
+
return await func(self, *args, **kwargs)
|
|
161
|
+
|
|
162
|
+
return wrapper
|
|
163
|
+
|
|
164
|
+
@abstractmethod
|
|
165
|
+
async def get_response(self, prompt: str) -> str:
|
|
166
|
+
"""
|
|
167
|
+
Abstract method to be implemented by subclasses to get a response from the connector.
|
|
168
|
+
|
|
169
|
+
This method should asynchronously send a prompt to the connector's API and return the response.
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
prompt (str): The input prompt to be sent to the connector.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
str: The response received from the connector.
|
|
176
|
+
"""
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def load(cls, ep_args: ConnectorEndpointArguments) -> Connector:
|
|
181
|
+
"""
|
|
182
|
+
This method dynamically loads a connector instance based on the provided endpoint arguments.
|
|
183
|
+
|
|
184
|
+
The connector type specified in the `ep_args` is used to dynamically load the corresponding
|
|
185
|
+
connector class. The connector is then instantiated with the provided endpoint arguments. If the
|
|
186
|
+
specified connector type does not correspond to any available connector classes, a RuntimeError is raised.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
ep_args (ConnectorEndpointArguments): The endpoint arguments which include the connector type and
|
|
190
|
+
other necessary information.
|
|
191
|
+
|
|
192
|
+
Returns:
|
|
193
|
+
Connector: An instance of the specified connector class, initialized with the given endpoint arguments.
|
|
194
|
+
|
|
195
|
+
Raises:
|
|
196
|
+
RuntimeError: If the specified connector type does not match any available connector classes.
|
|
197
|
+
"""
|
|
198
|
+
connector_instance = get_instance(
|
|
199
|
+
ep_args.connector_type,
|
|
200
|
+
Storage.get_filepath(
|
|
201
|
+
EnvVariables.CONNECTORS.name, ep_args.connector_type, "py"
|
|
202
|
+
),
|
|
203
|
+
)
|
|
204
|
+
if connector_instance:
|
|
205
|
+
return connector_instance(ep_args)
|
|
206
|
+
else:
|
|
207
|
+
raise RuntimeError(
|
|
208
|
+
f"Unable to get defined connector instance - {ep_args.connector_type}"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
@staticmethod
|
|
212
|
+
def create(ep_args: ConnectorEndpointArguments) -> Connector:
|
|
213
|
+
"""
|
|
214
|
+
Creates a connector object based on the provided endpoint arguments.
|
|
215
|
+
|
|
216
|
+
This method takes a ConnectorEndpointArguments object, which contains the necessary information
|
|
217
|
+
to initialize and return a Connector object. The Connector object is created by calling the
|
|
218
|
+
`load_connector` method, which dynamically loads and initializes the connector based on the
|
|
219
|
+
endpoint arguments provided.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
ep_args (ConnectorEndpointArguments): The endpoint arguments required to create the connector.
|
|
223
|
+
|
|
224
|
+
Returns:
|
|
225
|
+
Connector: An initialized Connector object based on the provided endpoint arguments.
|
|
226
|
+
"""
|
|
227
|
+
try:
|
|
228
|
+
return Connector.load(ep_args)
|
|
229
|
+
|
|
230
|
+
except Exception as e:
|
|
231
|
+
print(f"Failed to create connector: {str(e)}")
|
|
232
|
+
raise e
|
|
233
|
+
|
|
234
|
+
@staticmethod
|
|
235
|
+
def get_available_items() -> list[str]:
|
|
236
|
+
"""
|
|
237
|
+
Fetches a list of all available connector types.
|
|
238
|
+
|
|
239
|
+
This method employs the `get_connectors` method to locate all Python files in the directory
|
|
240
|
+
defined by the `EnvironmentVars.CONNECTORS` environment variable. It subsequently excludes any files that are
|
|
241
|
+
not intended to be exposed as connectors (those containing "__" in their names). The method yields a list of the
|
|
242
|
+
names of these connector types.
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
list[str]: A list of strings, each denoting the name of a connector type.
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
Exception: If an error occurs during the extraction of connector types.
|
|
249
|
+
"""
|
|
250
|
+
try:
|
|
251
|
+
return [
|
|
252
|
+
Path(fp).stem
|
|
253
|
+
for fp in Storage.get_objects(EnvVariables.CONNECTORS.name, "py")
|
|
254
|
+
if "__" not in fp
|
|
255
|
+
]
|
|
256
|
+
|
|
257
|
+
except Exception as e:
|
|
258
|
+
print(f"Failed to get available connectors: {str(e)}")
|
|
259
|
+
raise e
|
|
260
|
+
|
|
261
|
+
@staticmethod
|
|
262
|
+
async def get_prediction(
|
|
263
|
+
generated_prompt: ConnectorPromptArguments,
|
|
264
|
+
connector: Connector,
|
|
265
|
+
prompt_callback: Callable | None = None,
|
|
266
|
+
) -> ConnectorPromptArguments:
|
|
267
|
+
"""
|
|
268
|
+
Generates a prediction for a given prompt using a specified connector.
|
|
269
|
+
|
|
270
|
+
This method takes a `generated_prompt` object, which contains the prompt to be predicted, and a `connector`
|
|
271
|
+
object, which is used to generate the prediction. The method also optionally takes a `prompt_callback` function,
|
|
272
|
+
which is called after the prediction is generated.
|
|
273
|
+
|
|
274
|
+
The method first prints a message indicating that it is predicting the prompt. It then records the start time
|
|
275
|
+
and uses the `connector` to generate a prediction for the `generated_prompt`. The duration of the prediction
|
|
276
|
+
is calculated and stored in the `generated_prompt`.
|
|
277
|
+
|
|
278
|
+
If a `prompt_callback` function is provided, it is called with the `generated_prompt` and `connector.id` as
|
|
279
|
+
arguments.
|
|
280
|
+
|
|
281
|
+
The method then returns the `generated_prompt` with the generated prediction and duration.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
generated_prompt (ConnectorPromptArguments): The prompt to be predicted.
|
|
285
|
+
connector (Connector): The connector to be used for prediction.
|
|
286
|
+
prompt_callback (Callable | None): An optional callback function to be called after prediction.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
ConnectorPromptArguments: The `generated_prompt` with the generated prediction and duration.
|
|
290
|
+
|
|
291
|
+
Raises:
|
|
292
|
+
Exception: If there is an error during prediction.
|
|
293
|
+
"""
|
|
294
|
+
try:
|
|
295
|
+
print(f"Predicting prompt {generated_prompt.prompt_index} [{connector.id}]")
|
|
296
|
+
|
|
297
|
+
start_time = time.perf_counter()
|
|
298
|
+
generated_prompt.predicted_results = await connector.get_response(
|
|
299
|
+
generated_prompt.prompt
|
|
300
|
+
)
|
|
301
|
+
generated_prompt.duration = time.perf_counter() - start_time
|
|
302
|
+
print(
|
|
303
|
+
f"[Prompt {generated_prompt.prompt_index}] took {generated_prompt.duration:.4f}s"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Call prompt callback
|
|
307
|
+
if prompt_callback:
|
|
308
|
+
prompt_callback(generated_prompt, connector.id)
|
|
309
|
+
|
|
310
|
+
# Return the updated prompt
|
|
311
|
+
return generated_prompt
|
|
312
|
+
|
|
313
|
+
except Exception as e:
|
|
314
|
+
print(f"Failed to get prediction: {str(e)}")
|
|
315
|
+
raise e
|
|
316
|
+
|
|
317
|
+
def set_system_prompt(self, system_prompt: str) -> None:
|
|
318
|
+
"""
|
|
319
|
+
Assigns a new system prompt to this connector instance.
|
|
320
|
+
|
|
321
|
+
The system prompt serves as a preconfigured command or message that the connector can use to initiate
|
|
322
|
+
interactions or execute specific operations.
|
|
323
|
+
|
|
324
|
+
Parameters:
|
|
325
|
+
system_prompt (str): The new system prompt to set for this connector.
|
|
326
|
+
"""
|
|
327
|
+
self.system_prompt = system_prompt
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ConnectorPromptArguments(BaseModel):
|
|
9
|
+
prompt_index: int # The index of the prompt in the dataset
|
|
10
|
+
|
|
11
|
+
prompt: str # The actual prompt text
|
|
12
|
+
|
|
13
|
+
target: Any # The target response for the prompt
|
|
14
|
+
|
|
15
|
+
predicted_results: Any = "" # The predicted results, default is an empty string
|
|
16
|
+
|
|
17
|
+
duration: float = 0.0 # The duration it took to get the results, default is 0.0
|
|
File without changes
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
from pydantic import validate_call
|
|
4
|
+
from slugify import slugify
|
|
5
|
+
|
|
6
|
+
from moonshot.src.configs.env_variables import EnvVariables
|
|
7
|
+
from moonshot.src.connectors_endpoints.connector_endpoint_arguments import (
|
|
8
|
+
ConnectorEndpointArguments,
|
|
9
|
+
)
|
|
10
|
+
from moonshot.src.storage.storage import Storage
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConnectorEndpoint:
|
|
14
|
+
@staticmethod
|
|
15
|
+
def create(ep_args: ConnectorEndpointArguments) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Creates a new connector endpoint.
|
|
18
|
+
|
|
19
|
+
This method takes a ConnectorEndpointArguments object as input, generates a unique slugified ID based on the
|
|
20
|
+
endpoint's name, and then creates a new endpoint with the provided details. The endpoint information is stored
|
|
21
|
+
as a JSON object in the directory specified by `EnvVariables.CONNECTORS_ENDPOINTS`. If the operation is
|
|
22
|
+
successful, the unique ID of the new endpoint is returned. If any error arises during the process, an exception
|
|
23
|
+
is raised and the error message is logged.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
ep_args (ConnectorEndpointArguments): An object containing the details of the endpoint to be created.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
str: The unique ID of the newly created endpoint.
|
|
30
|
+
|
|
31
|
+
Raises:
|
|
32
|
+
Exception: If there's an error during the endpoint creation process.
|
|
33
|
+
"""
|
|
34
|
+
try:
|
|
35
|
+
ep_id = slugify(ep_args.name, lowercase=True)
|
|
36
|
+
ep_info = {
|
|
37
|
+
"id": ep_id,
|
|
38
|
+
"name": ep_args.name,
|
|
39
|
+
"connector_type": ep_args.connector_type,
|
|
40
|
+
"uri": ep_args.uri,
|
|
41
|
+
"token": ep_args.token,
|
|
42
|
+
"max_calls_per_second": ep_args.max_calls_per_second,
|
|
43
|
+
"max_concurrency": ep_args.max_concurrency,
|
|
44
|
+
"params": ep_args.params,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Write as json output
|
|
48
|
+
Storage.create_object(
|
|
49
|
+
EnvVariables.CONNECTORS_ENDPOINTS.name, ep_id, ep_info, "json"
|
|
50
|
+
)
|
|
51
|
+
return ep_id
|
|
52
|
+
|
|
53
|
+
except Exception as e:
|
|
54
|
+
print(f"Failed to create endpoint: {str(e)}")
|
|
55
|
+
raise e
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
@validate_call
|
|
59
|
+
def read(ep_id: str) -> ConnectorEndpointArguments:
|
|
60
|
+
"""
|
|
61
|
+
Fetches the details of a given endpoint.
|
|
62
|
+
|
|
63
|
+
This method takes an endpoint ID as input, finds the corresponding JSON file in the directory
|
|
64
|
+
specified by `EnvironmentVars.CONNECTORS_ENDPOINTS`, and returns a ConnectorEndpointArguments object
|
|
65
|
+
that contains the endpoint's details. If any error arises during the process, an exception is raised and the
|
|
66
|
+
error message is logged.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
ep_id (str): The unique ID of the endpoint to be fetched.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
ConnectorEndpointArguments: An object encapsulating the details of the fetched endpoint.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
Exception: If there's an error during the file reading process or any other operation within the method.
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
if ep_id:
|
|
79
|
+
return ConnectorEndpointArguments(
|
|
80
|
+
**ConnectorEndpoint._read_endpoint(ep_id)
|
|
81
|
+
)
|
|
82
|
+
else:
|
|
83
|
+
raise RuntimeError("Connector Endpoint ID is empty")
|
|
84
|
+
|
|
85
|
+
except Exception as e:
|
|
86
|
+
print(f"Failed to read endpoint: {str(e)}")
|
|
87
|
+
raise e
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def _read_endpoint(ep_id: str) -> dict:
|
|
91
|
+
"""
|
|
92
|
+
Reads the endpoint information from a JSON file and adds the creation datetime.
|
|
93
|
+
|
|
94
|
+
This method accepts an endpoint ID as an argument, locates the corresponding JSON file in the directory
|
|
95
|
+
defined by `EnvironmentVars.CONNECTORS_ENDPOINTS`, and returns a dictionary that encapsulates the endpoint's
|
|
96
|
+
details along with its creation datetime. If any error occurs during the process, it is handled by the calling
|
|
97
|
+
method.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
ep_id (str): The unique identifier of the endpoint to be retrieved.
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
dict: A dictionary containing the details of the retrieved endpoint along with its creation datetime.
|
|
104
|
+
"""
|
|
105
|
+
connector_endpoint_info = Storage.read_object(
|
|
106
|
+
EnvVariables.CONNECTORS_ENDPOINTS.name, ep_id, "json"
|
|
107
|
+
)
|
|
108
|
+
creation_datetime = Storage.get_creation_datetime(
|
|
109
|
+
EnvVariables.CONNECTORS_ENDPOINTS.name, ep_id, "json"
|
|
110
|
+
)
|
|
111
|
+
connector_endpoint_info["created_date"] = creation_datetime.replace(
|
|
112
|
+
microsecond=0
|
|
113
|
+
).isoformat(" ")
|
|
114
|
+
return connector_endpoint_info
|
|
115
|
+
|
|
116
|
+
@staticmethod
|
|
117
|
+
def update(ep_args: ConnectorEndpointArguments) -> bool:
|
|
118
|
+
"""
|
|
119
|
+
Updates the endpoint information based on the provided arguments.
|
|
120
|
+
|
|
121
|
+
This method takes a ConnectorEndpointArguments object, converts it to a dictionary, and removes the
|
|
122
|
+
'created_date' key if it exists. It then writes the updated information to the corresponding JSON file
|
|
123
|
+
in the directory specified by `EnvVariables.CONNECTORS_ENDPOINTS`.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
ep_args (ConnectorEndpointArguments): An object containing the updated details of the endpoint.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
bool: True if the update operation was successful.
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
Exception: If there's an error during the update process.
|
|
133
|
+
"""
|
|
134
|
+
try:
|
|
135
|
+
# Convert the endpoint arguments to a dictionary
|
|
136
|
+
# Remove created_date if it exists
|
|
137
|
+
ep_info = ep_args.to_dict()
|
|
138
|
+
ep_info.pop("created_date", None)
|
|
139
|
+
|
|
140
|
+
# Write the updated endpoint information to the file
|
|
141
|
+
Storage.create_object(
|
|
142
|
+
EnvVariables.CONNECTORS_ENDPOINTS.name, ep_args.id, ep_info, "json"
|
|
143
|
+
)
|
|
144
|
+
return True
|
|
145
|
+
|
|
146
|
+
except Exception as e:
|
|
147
|
+
print(f"Failed to update endpoint: {str(e)}")
|
|
148
|
+
raise e
|
|
149
|
+
|
|
150
|
+
@staticmethod
|
|
151
|
+
@validate_call
|
|
152
|
+
def delete(ep_id: str) -> bool:
|
|
153
|
+
"""
|
|
154
|
+
Deletes the endpoint with the specified ID.
|
|
155
|
+
|
|
156
|
+
This method attempts to delete the endpoint corresponding to the given ID from the storage.
|
|
157
|
+
If the deletion is successful, it returns True. If an error occurs, it prints an error message
|
|
158
|
+
and re-raises the exception.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
ep_id (str): The unique identifier of the endpoint to be deleted.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
bool: True if the endpoint was successfully deleted.
|
|
165
|
+
|
|
166
|
+
Raises:
|
|
167
|
+
Exception: If the deletion process encounters an error.
|
|
168
|
+
"""
|
|
169
|
+
try:
|
|
170
|
+
Storage.delete_object(EnvVariables.CONNECTORS_ENDPOINTS.name, ep_id, "json")
|
|
171
|
+
return True
|
|
172
|
+
|
|
173
|
+
except Exception as e:
|
|
174
|
+
print(f"Failed to delete endpoint: {str(e)}")
|
|
175
|
+
raise e
|
|
176
|
+
|
|
177
|
+
@staticmethod
|
|
178
|
+
def get_available_items() -> tuple[list[str], list[ConnectorEndpointArguments]]:
|
|
179
|
+
"""
|
|
180
|
+
Fetches the details of all available endpoints.
|
|
181
|
+
|
|
182
|
+
This method traverses the specified directory for connector endpoints, reads the information of each endpoint
|
|
183
|
+
from its corresponding JSON file, and assembles a list of endpoint IDs along with their respective details.
|
|
184
|
+
It excludes any files that are not valid endpoints (for instance, system files that begin with "__").
|
|
185
|
+
The method returns a tuple comprising a list of endpoint IDs and a list of ConnectorEndpointArguments objects,
|
|
186
|
+
each encapsulating the details of an endpoint.
|
|
187
|
+
|
|
188
|
+
Returns:
|
|
189
|
+
tuple[list[str], list[ConnectorEndpointArguments]]: A tuple containing a list of endpoint IDs and a list of
|
|
190
|
+
ConnectorEndpointArguments objects with endpoint details.
|
|
191
|
+
"""
|
|
192
|
+
try:
|
|
193
|
+
retn_eps = []
|
|
194
|
+
retn_eps_ids = []
|
|
195
|
+
|
|
196
|
+
eps = Storage.get_objects(EnvVariables.CONNECTORS_ENDPOINTS.name, "json")
|
|
197
|
+
for ep in eps:
|
|
198
|
+
if "__" in ep:
|
|
199
|
+
continue
|
|
200
|
+
|
|
201
|
+
ep_info = ConnectorEndpointArguments(
|
|
202
|
+
**ConnectorEndpoint._read_endpoint(Path(ep).stem)
|
|
203
|
+
)
|
|
204
|
+
retn_eps.append(ep_info)
|
|
205
|
+
retn_eps_ids.append(ep_info.id)
|
|
206
|
+
|
|
207
|
+
return retn_eps_ids, retn_eps
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
print(f"Failed to get available endpoints: {str(e)}")
|
|
211
|
+
raise e
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ConnectorEndpointArguments(BaseModel):
|
|
5
|
+
# id (str): The ID of the endpoint which is also the filename
|
|
6
|
+
# During creation, id is not required. The id is automatically generated and returned
|
|
7
|
+
id: str
|
|
8
|
+
|
|
9
|
+
name: str = Field(min_length=1) # name (str): The name for the endpoint.
|
|
10
|
+
|
|
11
|
+
connector_type: str # connector_type (str): The type of the LLM connector (e.g., 'GPT-3', 'Bert', etc.).
|
|
12
|
+
|
|
13
|
+
uri: str # uri (str): The URI (Uniform Resource Identifier) for the LLM connector's API.
|
|
14
|
+
|
|
15
|
+
token: str # token (str): The access token required to authenticate and access the LLM connector's API.
|
|
16
|
+
|
|
17
|
+
max_calls_per_second: int = Field(
|
|
18
|
+
gt=0
|
|
19
|
+
) # max_calls_per_second (int): The number of api calls per second
|
|
20
|
+
|
|
21
|
+
max_concurrency: int = Field(
|
|
22
|
+
gt=0
|
|
23
|
+
) # max_concurrency (int): The number of concurrent api calls
|
|
24
|
+
|
|
25
|
+
params: dict # params (dict): A dictionary that contains connection specified parameters
|
|
26
|
+
|
|
27
|
+
# created_date (str): The date and time the endpoint was created in isoformat without 'T'.
|
|
28
|
+
# During creation, created_date is not required. The created_date is automatically generated and returned
|
|
29
|
+
created_date: str = ""
|
|
30
|
+
|
|
31
|
+
def to_dict(self) -> dict:
|
|
32
|
+
"""
|
|
33
|
+
Converts the ConnectorEndpointArguments instance into a dictionary.
|
|
34
|
+
|
|
35
|
+
This method takes all the attributes of the ConnectorEndpointArguments instance and constructs a dictionary
|
|
36
|
+
with attribute names as keys and their corresponding values. This includes the id, name, connector_type, uri,
|
|
37
|
+
token, max_calls_per_second, max_concurrency, params, and created_date. This dictionary can be used for
|
|
38
|
+
serialization purposes, such as storing the endpoint information in a JSON file or sending it over a network.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
dict: A dictionary representation of the ConnectorEndpointArguments instance.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
"id": self.id,
|
|
46
|
+
"name": self.name,
|
|
47
|
+
"connector_type": self.connector_type,
|
|
48
|
+
"uri": self.uri,
|
|
49
|
+
"token": self.token,
|
|
50
|
+
"max_calls_per_second": self.max_calls_per_second,
|
|
51
|
+
"max_concurrency": self.max_concurrency,
|
|
52
|
+
"params": self.params,
|
|
53
|
+
"created_date": self.created_date,
|
|
54
|
+
}
|
|
File without changes
|