unique_toolkit 0.7.7__py3-none-any.whl → 1.23.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.
Potentially problematic release.
This version of unique_toolkit might be problematic. Click here for more details.
- unique_toolkit/__init__.py +28 -1
- unique_toolkit/_common/api_calling/human_verification_manager.py +343 -0
- unique_toolkit/_common/base_model_type_attribute.py +303 -0
- unique_toolkit/_common/chunk_relevancy_sorter/config.py +49 -0
- unique_toolkit/_common/chunk_relevancy_sorter/exception.py +5 -0
- unique_toolkit/_common/chunk_relevancy_sorter/schemas.py +46 -0
- unique_toolkit/_common/chunk_relevancy_sorter/service.py +374 -0
- unique_toolkit/_common/chunk_relevancy_sorter/tests/test_service.py +275 -0
- unique_toolkit/_common/default_language_model.py +12 -0
- unique_toolkit/_common/docx_generator/__init__.py +7 -0
- unique_toolkit/_common/docx_generator/config.py +12 -0
- unique_toolkit/_common/docx_generator/schemas.py +80 -0
- unique_toolkit/_common/docx_generator/service.py +252 -0
- unique_toolkit/_common/docx_generator/template/Doc Template.docx +0 -0
- unique_toolkit/_common/endpoint_builder.py +305 -0
- unique_toolkit/_common/endpoint_requestor.py +430 -0
- unique_toolkit/_common/exception.py +24 -0
- unique_toolkit/_common/feature_flags/schema.py +9 -0
- unique_toolkit/_common/pydantic/rjsf_tags.py +936 -0
- unique_toolkit/_common/pydantic_helpers.py +154 -0
- unique_toolkit/_common/referencing.py +53 -0
- unique_toolkit/_common/string_utilities.py +140 -0
- unique_toolkit/_common/tests/test_referencing.py +521 -0
- unique_toolkit/_common/tests/test_string_utilities.py +506 -0
- unique_toolkit/_common/token/image_token_counting.py +67 -0
- unique_toolkit/_common/token/token_counting.py +204 -0
- unique_toolkit/_common/utils/__init__.py +1 -0
- unique_toolkit/_common/utils/files.py +43 -0
- unique_toolkit/_common/utils/structured_output/__init__.py +1 -0
- unique_toolkit/_common/utils/structured_output/schema.py +5 -0
- unique_toolkit/_common/utils/write_configuration.py +51 -0
- unique_toolkit/_common/validators.py +101 -4
- unique_toolkit/agentic/__init__.py +1 -0
- unique_toolkit/agentic/debug_info_manager/debug_info_manager.py +28 -0
- unique_toolkit/agentic/debug_info_manager/test/test_debug_info_manager.py +278 -0
- unique_toolkit/agentic/evaluation/config.py +36 -0
- unique_toolkit/{evaluators → agentic/evaluation}/context_relevancy/prompts.py +25 -0
- unique_toolkit/agentic/evaluation/context_relevancy/schema.py +80 -0
- unique_toolkit/agentic/evaluation/context_relevancy/service.py +273 -0
- unique_toolkit/agentic/evaluation/evaluation_manager.py +218 -0
- unique_toolkit/agentic/evaluation/hallucination/constants.py +61 -0
- unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py +111 -0
- unique_toolkit/{evaluators → agentic/evaluation}/hallucination/prompts.py +1 -1
- unique_toolkit/{evaluators → agentic/evaluation}/hallucination/service.py +16 -15
- unique_toolkit/{evaluators → agentic/evaluation}/hallucination/utils.py +30 -20
- unique_toolkit/{evaluators → agentic/evaluation}/output_parser.py +20 -2
- unique_toolkit/{evaluators → agentic/evaluation}/schemas.py +27 -7
- unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py +253 -0
- unique_toolkit/agentic/evaluation/tests/test_output_parser.py +87 -0
- unique_toolkit/agentic/history_manager/history_construction_with_contents.py +297 -0
- unique_toolkit/agentic/history_manager/history_manager.py +242 -0
- unique_toolkit/agentic/history_manager/loop_token_reducer.py +484 -0
- unique_toolkit/agentic/history_manager/utils.py +96 -0
- unique_toolkit/agentic/postprocessor/postprocessor_manager.py +212 -0
- unique_toolkit/agentic/reference_manager/reference_manager.py +103 -0
- unique_toolkit/agentic/responses_api/__init__.py +19 -0
- unique_toolkit/agentic/responses_api/postprocessors/code_display.py +63 -0
- unique_toolkit/agentic/responses_api/postprocessors/generated_files.py +145 -0
- unique_toolkit/agentic/responses_api/stream_handler.py +15 -0
- unique_toolkit/agentic/short_term_memory_manager/persistent_short_term_memory_manager.py +141 -0
- unique_toolkit/agentic/thinking_manager/thinking_manager.py +103 -0
- unique_toolkit/agentic/tools/__init__.py +1 -0
- unique_toolkit/agentic/tools/a2a/__init__.py +36 -0
- unique_toolkit/agentic/tools/a2a/config.py +17 -0
- unique_toolkit/agentic/tools/a2a/evaluation/__init__.py +15 -0
- unique_toolkit/agentic/tools/a2a/evaluation/_utils.py +66 -0
- unique_toolkit/agentic/tools/a2a/evaluation/config.py +55 -0
- unique_toolkit/agentic/tools/a2a/evaluation/evaluator.py +260 -0
- unique_toolkit/agentic/tools/a2a/evaluation/summarization_user_message.j2 +9 -0
- unique_toolkit/agentic/tools/a2a/manager.py +55 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/__init__.py +21 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/_display_utils.py +185 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/_ref_utils.py +73 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/config.py +45 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/display.py +180 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/references.py +101 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_display_utils.py +1335 -0
- unique_toolkit/agentic/tools/a2a/postprocessing/test/test_ref_utils.py +603 -0
- unique_toolkit/agentic/tools/a2a/prompts.py +46 -0
- unique_toolkit/agentic/tools/a2a/response_watcher/__init__.py +6 -0
- unique_toolkit/agentic/tools/a2a/response_watcher/service.py +91 -0
- unique_toolkit/agentic/tools/a2a/tool/__init__.py +4 -0
- unique_toolkit/agentic/tools/a2a/tool/_memory.py +26 -0
- unique_toolkit/agentic/tools/a2a/tool/_schema.py +9 -0
- unique_toolkit/agentic/tools/a2a/tool/config.py +73 -0
- unique_toolkit/agentic/tools/a2a/tool/service.py +306 -0
- unique_toolkit/agentic/tools/agent_chunks_hanlder.py +65 -0
- unique_toolkit/agentic/tools/config.py +167 -0
- unique_toolkit/agentic/tools/factory.py +44 -0
- unique_toolkit/agentic/tools/mcp/__init__.py +4 -0
- unique_toolkit/agentic/tools/mcp/manager.py +71 -0
- unique_toolkit/agentic/tools/mcp/models.py +28 -0
- unique_toolkit/agentic/tools/mcp/tool_wrapper.py +234 -0
- unique_toolkit/agentic/tools/openai_builtin/__init__.py +11 -0
- unique_toolkit/agentic/tools/openai_builtin/base.py +30 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/__init__.py +8 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/config.py +57 -0
- unique_toolkit/agentic/tools/openai_builtin/code_interpreter/service.py +230 -0
- unique_toolkit/agentic/tools/openai_builtin/manager.py +62 -0
- unique_toolkit/agentic/tools/schemas.py +141 -0
- unique_toolkit/agentic/tools/test/test_mcp_manager.py +536 -0
- unique_toolkit/agentic/tools/test/test_tool_progress_reporter.py +445 -0
- unique_toolkit/agentic/tools/tool.py +183 -0
- unique_toolkit/agentic/tools/tool_manager.py +523 -0
- unique_toolkit/agentic/tools/tool_progress_reporter.py +285 -0
- unique_toolkit/agentic/tools/utils/__init__.py +19 -0
- unique_toolkit/agentic/tools/utils/execution/__init__.py +1 -0
- unique_toolkit/agentic/tools/utils/execution/execution.py +286 -0
- unique_toolkit/agentic/tools/utils/source_handling/__init__.py +0 -0
- unique_toolkit/agentic/tools/utils/source_handling/schema.py +21 -0
- unique_toolkit/agentic/tools/utils/source_handling/source_formatting.py +207 -0
- unique_toolkit/agentic/tools/utils/source_handling/tests/test_source_formatting.py +216 -0
- unique_toolkit/app/__init__.py +6 -0
- unique_toolkit/app/dev_util.py +180 -0
- unique_toolkit/app/init_sdk.py +32 -1
- unique_toolkit/app/schemas.py +198 -31
- unique_toolkit/app/unique_settings.py +367 -0
- unique_toolkit/chat/__init__.py +8 -1
- unique_toolkit/chat/deprecated/service.py +232 -0
- unique_toolkit/chat/functions.py +642 -77
- unique_toolkit/chat/rendering.py +34 -0
- unique_toolkit/chat/responses_api.py +461 -0
- unique_toolkit/chat/schemas.py +133 -2
- unique_toolkit/chat/service.py +115 -767
- unique_toolkit/content/functions.py +153 -4
- unique_toolkit/content/schemas.py +122 -15
- unique_toolkit/content/service.py +278 -44
- unique_toolkit/content/smart_rules.py +301 -0
- unique_toolkit/content/utils.py +8 -3
- unique_toolkit/embedding/service.py +102 -11
- unique_toolkit/framework_utilities/__init__.py +1 -0
- unique_toolkit/framework_utilities/langchain/client.py +71 -0
- unique_toolkit/framework_utilities/langchain/history.py +19 -0
- unique_toolkit/framework_utilities/openai/__init__.py +6 -0
- unique_toolkit/framework_utilities/openai/client.py +83 -0
- unique_toolkit/framework_utilities/openai/message_builder.py +229 -0
- unique_toolkit/framework_utilities/utils.py +23 -0
- unique_toolkit/language_model/__init__.py +3 -0
- unique_toolkit/language_model/builder.py +27 -11
- unique_toolkit/language_model/default_language_model.py +3 -0
- unique_toolkit/language_model/functions.py +327 -43
- unique_toolkit/language_model/infos.py +992 -50
- unique_toolkit/language_model/reference.py +242 -0
- unique_toolkit/language_model/schemas.py +475 -48
- unique_toolkit/language_model/service.py +228 -27
- unique_toolkit/protocols/support.py +145 -0
- unique_toolkit/services/__init__.py +7 -0
- unique_toolkit/services/chat_service.py +1630 -0
- unique_toolkit/services/knowledge_base.py +861 -0
- unique_toolkit/short_term_memory/service.py +178 -41
- unique_toolkit/smart_rules/__init__.py +0 -0
- unique_toolkit/smart_rules/compile.py +56 -0
- unique_toolkit/test_utilities/events.py +197 -0
- {unique_toolkit-0.7.7.dist-info → unique_toolkit-1.23.0.dist-info}/METADATA +606 -7
- unique_toolkit-1.23.0.dist-info/RECORD +182 -0
- unique_toolkit/evaluators/__init__.py +0 -1
- unique_toolkit/evaluators/config.py +0 -35
- unique_toolkit/evaluators/constants.py +0 -1
- unique_toolkit/evaluators/context_relevancy/constants.py +0 -32
- unique_toolkit/evaluators/context_relevancy/service.py +0 -53
- unique_toolkit/evaluators/context_relevancy/utils.py +0 -142
- unique_toolkit/evaluators/hallucination/constants.py +0 -41
- unique_toolkit-0.7.7.dist-info/RECORD +0 -64
- /unique_toolkit/{evaluators → agentic/evaluation}/exception.py +0 -0
- {unique_toolkit-0.7.7.dist-info → unique_toolkit-1.23.0.dist-info}/LICENSE +0 -0
- {unique_toolkit-0.7.7.dist-info → unique_toolkit-1.23.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
from enum import StrEnum
|
|
2
|
+
from typing import Any, Callable, Generic, Protocol, TypeVar
|
|
3
|
+
from urllib.parse import urljoin, urlparse
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
from typing_extensions import ParamSpec
|
|
7
|
+
|
|
8
|
+
from unique_toolkit._common.endpoint_builder import (
|
|
9
|
+
ApiOperationProtocol,
|
|
10
|
+
HttpMethods,
|
|
11
|
+
PathParamsSpec,
|
|
12
|
+
PathParamsType,
|
|
13
|
+
PayloadParamSpec,
|
|
14
|
+
PayloadType,
|
|
15
|
+
ResponseType,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
# Paramspecs
|
|
19
|
+
CombinedParamsSpec = ParamSpec("CombinedParamsSpec")
|
|
20
|
+
|
|
21
|
+
# Type variables
|
|
22
|
+
CombinedParamsType = TypeVar("CombinedParamsType", bound=BaseModel)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
ResponseT_co = TypeVar("ResponseT_co", bound=BaseModel, covariant=True)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RequestContext(BaseModel):
|
|
29
|
+
base_url: str
|
|
30
|
+
headers: dict[str, str] = Field(default_factory=dict)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _verify_url(url: str) -> None:
|
|
34
|
+
parse_result = urlparse(url)
|
|
35
|
+
if not (parse_result.netloc and parse_result.scheme):
|
|
36
|
+
raise ValueError("Scheme and netloc are required for url")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class EndpointRequestorProtocol(Protocol, Generic[CombinedParamsSpec, ResponseT_co]):
|
|
40
|
+
@classmethod
|
|
41
|
+
def request(
|
|
42
|
+
cls,
|
|
43
|
+
context: RequestContext,
|
|
44
|
+
*args: CombinedParamsSpec.args,
|
|
45
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
46
|
+
) -> ResponseT_co: ...
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
async def request_async(
|
|
50
|
+
cls,
|
|
51
|
+
context: RequestContext,
|
|
52
|
+
*args: CombinedParamsSpec.args,
|
|
53
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
54
|
+
) -> ResponseT_co: ...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def build_fake_requestor(
|
|
58
|
+
operation_type: type[
|
|
59
|
+
ApiOperationProtocol[
|
|
60
|
+
PathParamsSpec,
|
|
61
|
+
PathParamsType,
|
|
62
|
+
PayloadParamSpec,
|
|
63
|
+
PayloadType,
|
|
64
|
+
ResponseType,
|
|
65
|
+
]
|
|
66
|
+
],
|
|
67
|
+
combined_model: Callable[CombinedParamsSpec, CombinedParamsType],
|
|
68
|
+
return_value: dict[str, Any],
|
|
69
|
+
) -> type[EndpointRequestorProtocol[CombinedParamsSpec, ResponseType]]:
|
|
70
|
+
class FakeRequestor(EndpointRequestorProtocol):
|
|
71
|
+
_operation = operation_type
|
|
72
|
+
|
|
73
|
+
@classmethod
|
|
74
|
+
def request(
|
|
75
|
+
cls,
|
|
76
|
+
context: RequestContext,
|
|
77
|
+
*args: CombinedParamsSpec.args,
|
|
78
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
79
|
+
) -> ResponseType:
|
|
80
|
+
try:
|
|
81
|
+
path_params, payload_model = cls._operation.models_from_combined(
|
|
82
|
+
combined=kwargs
|
|
83
|
+
)
|
|
84
|
+
except Exception as e:
|
|
85
|
+
raise ValueError(
|
|
86
|
+
f"Invalid parameters passed to combined model {combined_model.__name__}: {e}"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return cls._operation.handle_response(
|
|
90
|
+
return_value,
|
|
91
|
+
model_validate_options=cls._operation.response_validate_options(),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@classmethod
|
|
95
|
+
async def request_async(
|
|
96
|
+
cls,
|
|
97
|
+
context: RequestContext,
|
|
98
|
+
headers: dict[str, str] | None = None,
|
|
99
|
+
*args: CombinedParamsSpec.args,
|
|
100
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
101
|
+
) -> ResponseType:
|
|
102
|
+
raise NotImplementedError(
|
|
103
|
+
"Async request not implemented for fake requestor"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
return FakeRequestor
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def build_request_requestor(
|
|
110
|
+
operation_type: type[
|
|
111
|
+
ApiOperationProtocol[
|
|
112
|
+
PathParamsSpec,
|
|
113
|
+
PathParamsType,
|
|
114
|
+
PayloadParamSpec,
|
|
115
|
+
PayloadType,
|
|
116
|
+
ResponseType,
|
|
117
|
+
]
|
|
118
|
+
],
|
|
119
|
+
combined_model: Callable[CombinedParamsSpec, CombinedParamsType],
|
|
120
|
+
) -> type[EndpointRequestorProtocol[CombinedParamsSpec, ResponseType]]:
|
|
121
|
+
import requests
|
|
122
|
+
|
|
123
|
+
class RequestRequestor(EndpointRequestorProtocol):
|
|
124
|
+
_operation = operation_type
|
|
125
|
+
|
|
126
|
+
@classmethod
|
|
127
|
+
def request(
|
|
128
|
+
cls,
|
|
129
|
+
context: RequestContext,
|
|
130
|
+
*args: CombinedParamsSpec.args,
|
|
131
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
132
|
+
) -> ResponseType:
|
|
133
|
+
# Create separate instances for path params and payload using endpoint helper
|
|
134
|
+
path_params, payload_model = cls._operation.models_from_combined(
|
|
135
|
+
combined=kwargs
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
path = cls._operation.create_path_from_model(
|
|
139
|
+
path_params, model_dump_options=cls._operation.path_dump_options()
|
|
140
|
+
)
|
|
141
|
+
url = urljoin(context.base_url, path)
|
|
142
|
+
_verify_url(url)
|
|
143
|
+
|
|
144
|
+
payload = cls._operation.create_payload_from_model(
|
|
145
|
+
payload_model, model_dump_options=cls._operation.payload_dump_options()
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
response = requests.request(
|
|
149
|
+
method=cls._operation.request_method(),
|
|
150
|
+
url=url,
|
|
151
|
+
headers=context.headers,
|
|
152
|
+
json=payload,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
response_json = response.json()
|
|
156
|
+
|
|
157
|
+
return cls._operation.handle_response(
|
|
158
|
+
response_json,
|
|
159
|
+
model_validate_options=cls._operation.response_validate_options(),
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
@classmethod
|
|
163
|
+
async def request_async(
|
|
164
|
+
cls,
|
|
165
|
+
base_url: str = "",
|
|
166
|
+
headers: dict[str, str] | None = None,
|
|
167
|
+
*args: CombinedParamsSpec.args,
|
|
168
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
169
|
+
) -> ResponseType:
|
|
170
|
+
raise NotImplementedError(
|
|
171
|
+
"Async request not implemented for request requestor"
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return RequestRequestor
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def build_httpx_requestor(
|
|
178
|
+
operation_type: type[
|
|
179
|
+
ApiOperationProtocol[
|
|
180
|
+
PathParamsSpec,
|
|
181
|
+
PathParamsType,
|
|
182
|
+
PayloadParamSpec,
|
|
183
|
+
PayloadType,
|
|
184
|
+
ResponseType,
|
|
185
|
+
]
|
|
186
|
+
],
|
|
187
|
+
combined_model: Callable[CombinedParamsSpec, CombinedParamsType],
|
|
188
|
+
) -> type[EndpointRequestorProtocol[CombinedParamsSpec, ResponseType]]:
|
|
189
|
+
import httpx
|
|
190
|
+
|
|
191
|
+
class HttpxRequestor(EndpointRequestorProtocol):
|
|
192
|
+
_operation = operation_type
|
|
193
|
+
|
|
194
|
+
@classmethod
|
|
195
|
+
def request(
|
|
196
|
+
cls,
|
|
197
|
+
context: RequestContext,
|
|
198
|
+
*args: CombinedParamsSpec.args,
|
|
199
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
200
|
+
) -> ResponseType:
|
|
201
|
+
headers = context.headers or {}
|
|
202
|
+
|
|
203
|
+
path_params, payload_model = cls._operation.models_from_combined(
|
|
204
|
+
combined=kwargs
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
path = cls._operation.create_path_from_model(
|
|
208
|
+
path_params, model_dump_options=cls._operation.path_dump_options()
|
|
209
|
+
)
|
|
210
|
+
url = urljoin(context.base_url, path)
|
|
211
|
+
_verify_url(url)
|
|
212
|
+
with httpx.Client() as client:
|
|
213
|
+
response = client.request(
|
|
214
|
+
method=cls._operation.request_method(),
|
|
215
|
+
url=url,
|
|
216
|
+
headers=headers,
|
|
217
|
+
json=cls._operation.create_payload_from_model(
|
|
218
|
+
payload_model,
|
|
219
|
+
model_dump_options=cls._operation.payload_dump_options(),
|
|
220
|
+
),
|
|
221
|
+
)
|
|
222
|
+
response_json = response.json()
|
|
223
|
+
return cls._operation.handle_response(
|
|
224
|
+
response_json,
|
|
225
|
+
model_validate_options=cls._operation.response_validate_options(),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
@classmethod
|
|
229
|
+
async def request_async(
|
|
230
|
+
cls,
|
|
231
|
+
context: RequestContext,
|
|
232
|
+
*args: CombinedParamsSpec.args,
|
|
233
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
234
|
+
) -> ResponseType:
|
|
235
|
+
headers = context.headers or {}
|
|
236
|
+
|
|
237
|
+
path_params, payload_model = cls._operation.models_from_combined(
|
|
238
|
+
combined=kwargs
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
path = cls._operation.create_path_from_model(
|
|
242
|
+
path_params, model_dump_options=cls._operation.path_dump_options()
|
|
243
|
+
)
|
|
244
|
+
url = urljoin(context.base_url, path)
|
|
245
|
+
_verify_url(url)
|
|
246
|
+
async with httpx.AsyncClient() as client:
|
|
247
|
+
response = await client.request(
|
|
248
|
+
method=cls._operation.request_method(),
|
|
249
|
+
url=url,
|
|
250
|
+
headers=headers,
|
|
251
|
+
json=cls._operation.create_payload_from_model(
|
|
252
|
+
payload_model,
|
|
253
|
+
model_dump_options=cls._operation.payload_dump_options(),
|
|
254
|
+
),
|
|
255
|
+
)
|
|
256
|
+
response_json = response.json()
|
|
257
|
+
return cls._operation.handle_response(
|
|
258
|
+
response_json,
|
|
259
|
+
model_validate_options=cls._operation.response_validate_options(),
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
return HttpxRequestor
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def build_aiohttp_requestor(
|
|
266
|
+
operation_type: type[
|
|
267
|
+
ApiOperationProtocol[
|
|
268
|
+
PathParamsSpec,
|
|
269
|
+
PathParamsType,
|
|
270
|
+
PayloadParamSpec,
|
|
271
|
+
PayloadType,
|
|
272
|
+
ResponseType,
|
|
273
|
+
]
|
|
274
|
+
],
|
|
275
|
+
combined_model: Callable[CombinedParamsSpec, CombinedParamsType],
|
|
276
|
+
) -> type[EndpointRequestorProtocol[CombinedParamsSpec, ResponseType]]:
|
|
277
|
+
import aiohttp
|
|
278
|
+
|
|
279
|
+
class AiohttpRequestor(EndpointRequestorProtocol):
|
|
280
|
+
_operation = operation_type
|
|
281
|
+
|
|
282
|
+
@classmethod
|
|
283
|
+
def request(
|
|
284
|
+
cls,
|
|
285
|
+
context: RequestContext,
|
|
286
|
+
*args: CombinedParamsSpec.args,
|
|
287
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
288
|
+
) -> ResponseType:
|
|
289
|
+
raise NotImplementedError(
|
|
290
|
+
"Sync request not implemented for aiohttp requestor"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
@classmethod
|
|
294
|
+
async def request_async(
|
|
295
|
+
cls,
|
|
296
|
+
context: RequestContext,
|
|
297
|
+
headers: dict[str, str] | None = None,
|
|
298
|
+
*args: CombinedParamsSpec.args,
|
|
299
|
+
**kwargs: CombinedParamsSpec.kwargs,
|
|
300
|
+
) -> ResponseType:
|
|
301
|
+
headers = context.headers or {}
|
|
302
|
+
|
|
303
|
+
path_params, payload_model = cls._operation.models_from_combined(
|
|
304
|
+
combined=kwargs
|
|
305
|
+
)
|
|
306
|
+
path = cls._operation.create_path_from_model(
|
|
307
|
+
path_params, model_dump_options=cls._operation.path_dump_options()
|
|
308
|
+
)
|
|
309
|
+
url = urljoin(context.base_url, path)
|
|
310
|
+
_verify_url(url)
|
|
311
|
+
|
|
312
|
+
async with aiohttp.ClientSession() as session:
|
|
313
|
+
response = await session.request(
|
|
314
|
+
method=cls._operation.request_method(),
|
|
315
|
+
url=url,
|
|
316
|
+
headers=headers,
|
|
317
|
+
json=cls._operation.create_payload_from_model(
|
|
318
|
+
payload=payload_model,
|
|
319
|
+
model_dump_options=cls._operation.payload_dump_options(),
|
|
320
|
+
),
|
|
321
|
+
)
|
|
322
|
+
response_json = await response.json()
|
|
323
|
+
return cls._operation.handle_response(
|
|
324
|
+
response=response_json,
|
|
325
|
+
model_validate_options=cls._operation.response_validate_options(),
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
return AiohttpRequestor
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class RequestorType(StrEnum):
|
|
332
|
+
REQUESTS = "requests"
|
|
333
|
+
FAKE = "fake"
|
|
334
|
+
HTTPIX = "httpx"
|
|
335
|
+
AIOHTTP = "aiohttp"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def build_requestor(
|
|
339
|
+
requestor_type: RequestorType,
|
|
340
|
+
operation_type: type[
|
|
341
|
+
ApiOperationProtocol[
|
|
342
|
+
PathParamsSpec,
|
|
343
|
+
PathParamsType,
|
|
344
|
+
PayloadParamSpec,
|
|
345
|
+
PayloadType,
|
|
346
|
+
ResponseType,
|
|
347
|
+
]
|
|
348
|
+
],
|
|
349
|
+
combined_model: Callable[CombinedParamsSpec, CombinedParamsType],
|
|
350
|
+
return_value: dict[str, Any] | None = None,
|
|
351
|
+
**kwargs: Any,
|
|
352
|
+
) -> type[EndpointRequestorProtocol]:
|
|
353
|
+
match requestor_type:
|
|
354
|
+
case RequestorType.REQUESTS:
|
|
355
|
+
return build_request_requestor(
|
|
356
|
+
operation_type=operation_type, combined_model=combined_model
|
|
357
|
+
)
|
|
358
|
+
case RequestorType.FAKE:
|
|
359
|
+
if return_value is None:
|
|
360
|
+
raise ValueError("return_value is required for fake requestor")
|
|
361
|
+
return build_fake_requestor(
|
|
362
|
+
operation_type=operation_type,
|
|
363
|
+
combined_model=combined_model,
|
|
364
|
+
return_value=return_value,
|
|
365
|
+
)
|
|
366
|
+
case RequestorType.HTTPIX:
|
|
367
|
+
return build_httpx_requestor(
|
|
368
|
+
operation_type=operation_type, combined_model=combined_model
|
|
369
|
+
)
|
|
370
|
+
case RequestorType.AIOHTTP:
|
|
371
|
+
return build_aiohttp_requestor(
|
|
372
|
+
operation_type=operation_type, combined_model=combined_model
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
if __name__ == "__main__":
|
|
377
|
+
from string import Template
|
|
378
|
+
|
|
379
|
+
from unique_toolkit._common.endpoint_builder import build_api_operation
|
|
380
|
+
|
|
381
|
+
class GetUserPathParams(BaseModel):
|
|
382
|
+
user_id: int
|
|
383
|
+
|
|
384
|
+
class GetUserRequestBody(BaseModel):
|
|
385
|
+
include_profile: bool = False
|
|
386
|
+
|
|
387
|
+
class UserResponse(BaseModel):
|
|
388
|
+
id: int
|
|
389
|
+
name: str
|
|
390
|
+
|
|
391
|
+
class CombinedParams(GetUserPathParams, GetUserRequestBody):
|
|
392
|
+
pass
|
|
393
|
+
|
|
394
|
+
UserEndpoint = build_api_operation(
|
|
395
|
+
method=HttpMethods.GET,
|
|
396
|
+
path_template=Template("/users/{user_id}"),
|
|
397
|
+
path_params_constructor=GetUserPathParams,
|
|
398
|
+
payload_constructor=GetUserRequestBody,
|
|
399
|
+
response_model_type=UserResponse,
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
FakeUserRequestor = build_fake_requestor(
|
|
403
|
+
operation_type=UserEndpoint,
|
|
404
|
+
combined_model=CombinedParams,
|
|
405
|
+
return_value={"id": 100, "name": "John Doe"},
|
|
406
|
+
)
|
|
407
|
+
|
|
408
|
+
# Note that the return value is a pydantic UserResponse object
|
|
409
|
+
response = FakeUserRequestor().request(
|
|
410
|
+
context=RequestContext(base_url="https://example.com", headers={"a": "b"}),
|
|
411
|
+
user_id=123,
|
|
412
|
+
include_profile=True,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
RequestRequestor = build_request_requestor(
|
|
416
|
+
operation_type=UserEndpoint,
|
|
417
|
+
combined_model=CombinedParams,
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
# Check type hints
|
|
421
|
+
response = RequestRequestor().request(
|
|
422
|
+
context=RequestContext(base_url="https://example.com", headers={"a": "b"}),
|
|
423
|
+
user_id=123,
|
|
424
|
+
include_profile=True,
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
print(response.model_dump())
|
|
428
|
+
print(response.model_json_schema())
|
|
429
|
+
print(response.id)
|
|
430
|
+
print(response.name)
|
|
@@ -31,3 +31,27 @@ class CommonException(Exception):
|
|
|
31
31
|
|
|
32
32
|
def __str__(self):
|
|
33
33
|
return self._error_message
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ConfigurationException(Exception):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class InfoExceptionForAi(Exception):
|
|
41
|
+
"""
|
|
42
|
+
This exception is raised as information to the AI.
|
|
43
|
+
Such that it can be used to inform the user about the error.
|
|
44
|
+
In a meaningful way.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
error_message: str,
|
|
50
|
+
message_for_ai: str,
|
|
51
|
+
):
|
|
52
|
+
super().__init__(error_message)
|
|
53
|
+
self._message_for_ai = message_for_ai
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def message_for_ai(self):
|
|
57
|
+
return self._message_for_ai
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from unique_toolkit.agentic.tools.config import get_configuration_dict
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FeatureExtendedSourceSerialization(BaseModel):
|
|
7
|
+
"""Mixin for experimental feature in Source serialization"""
|
|
8
|
+
|
|
9
|
+
model_config = get_configuration_dict()
|