unique_toolkit 1.11.4__py3-none-any.whl → 1.12.1__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.
@@ -123,6 +123,15 @@ class EvaluationManager:
123
123
  self._evaluation_passed = False
124
124
  evaluation_results_unpacked.append(unpacked_evaluation_result)
125
125
 
126
+ for evaluation_name, evaluation_result in zip(
127
+ selected_evaluation_names, evaluation_results_unpacked
128
+ ):
129
+ evaluation_instance = self.get_evaluation_by_name(evaluation_name)
130
+ if evaluation_instance:
131
+ await self._show_message_assessment(
132
+ evaluation_instance, evaluation_result, assistant_message_id
133
+ )
134
+
126
135
  return evaluation_results_unpacked
127
136
 
128
137
  async def execute_evaluation_call(
@@ -143,11 +152,6 @@ class EvaluationManager:
143
152
  evaluation_metric_result: EvaluationMetricResult = (
144
153
  await evaluation_instance.run(loop_response)
145
154
  )
146
- # show results to the user
147
- await self._show_message_assessment(
148
- evaluation_instance, evaluation_metric_result, assistant_message_id
149
- )
150
-
151
155
  return evaluation_metric_result
152
156
 
153
157
  return EvaluationMetricResult(
@@ -78,11 +78,19 @@ class HallucinationEvaluation(Evaluation):
78
78
  if not evaluation_result.error
79
79
  else ChatMessageAssessmentStatus.ERROR
80
80
  )
81
+ explanation = evaluation_result.reason
82
+
83
+ if status == ChatMessageAssessmentStatus.ERROR:
84
+ title = "Hallucination Check Error"
85
+ label = ChatMessageAssessmentLabel.RED
86
+ explanation = (
87
+ "An unrecoverable error occurred while evaluating the response."
88
+ )
81
89
 
82
90
  return EvaluationAssessmentMessage(
83
91
  status=status,
84
92
  title=title,
85
- explanation=evaluation_result.reason,
93
+ explanation=explanation,
86
94
  label=label,
87
95
  type=self.get_assessment_type(),
88
96
  )
@@ -1,5 +1,8 @@
1
+ import base64
2
+ import mimetypes
1
3
  from collections.abc import Iterable
2
- from typing import Self
4
+ from pathlib import Path
5
+ from typing import Self, overload
3
6
 
4
7
  from openai.types.chat.chat_completion_assistant_message_param import (
5
8
  Audio,
@@ -7,6 +10,10 @@ from openai.types.chat.chat_completion_assistant_message_param import (
7
10
  ContentArrayOfContentPart,
8
11
  FunctionCall,
9
12
  )
13
+ from openai.types.chat.chat_completion_content_part_image_param import (
14
+ ChatCompletionContentPartImageParam,
15
+ ImageURL,
16
+ )
10
17
  from openai.types.chat.chat_completion_content_part_param import (
11
18
  ChatCompletionContentPartParam,
12
19
  )
@@ -32,6 +39,83 @@ from openai.types.chat.chat_completion_tool_message_param import (
32
39
  from openai.types.chat.chat_completion_user_message_param import (
33
40
  ChatCompletionUserMessageParam,
34
41
  )
42
+ from typing_extensions import Literal
43
+
44
+
45
+ class OpenAIUserMessageBuilder:
46
+ def __init__(
47
+ self,
48
+ ) -> None:
49
+ self._messages: list[ChatCompletionContentPartParam] = []
50
+
51
+ def append_text(self, content: str) -> Self:
52
+ part = ChatCompletionContentPartTextParam(
53
+ type="text",
54
+ text=content,
55
+ )
56
+ self._messages.append(part)
57
+ return self
58
+
59
+ @overload
60
+ def append_image(
61
+ self, *, url: str, detail: Literal["auto", "low", "high"] = "auto"
62
+ ) -> Self: ...
63
+
64
+ @overload
65
+ def append_image(
66
+ self, *, path: Path, detail: Literal["auto", "low", "high"] = "auto"
67
+ ) -> Self: ...
68
+
69
+ @overload
70
+ def append_image(
71
+ self,
72
+ *,
73
+ content: bytes,
74
+ mime_type: str,
75
+ detail: Literal["auto", "low", "high"] = "auto",
76
+ ) -> Self: ...
77
+
78
+ def append_image(
79
+ self,
80
+ *,
81
+ url: str | None = None,
82
+ path: Path | None = None,
83
+ content: bytes | None = None,
84
+ mime_type: str | None = None,
85
+ detail: Literal["auto", "low", "high"] = "auto",
86
+ ) -> Self:
87
+ if url is None and path is None and (content is None or mime_type is None):
88
+ raise ValueError("Either url or path must be provided")
89
+
90
+ if path is not None:
91
+ # Read image file and encode as base64 data URI
92
+ image_data = path.read_bytes()
93
+ base64_image = base64.b64encode(image_data).decode("utf-8")
94
+ mime_type = mimetypes.guess_type(str(path))[0] or "image/jpeg"
95
+ url = f"data:{mime_type};base64,{base64_image}"
96
+
97
+ if content is not None and mime_type is not None:
98
+ base64_image = base64.b64encode(content).decode("utf-8")
99
+ url = f"data:{mime_type};base64,{base64_image}"
100
+
101
+ image_url = ImageURL(url=url or "", detail=detail)
102
+ part = ChatCompletionContentPartImageParam(
103
+ type="image_url",
104
+ image_url=image_url,
105
+ )
106
+ self._messages.append(part)
107
+ return self
108
+
109
+ @property
110
+ def user_message(self) -> ChatCompletionUserMessageParam:
111
+ return ChatCompletionUserMessageParam(
112
+ content=self._messages,
113
+ role="user",
114
+ )
115
+
116
+ @property
117
+ def iterable_content(self) -> Iterable[ChatCompletionContentPartParam]:
118
+ return self._messages
35
119
 
36
120
 
37
121
  class OpenAIMessageBuilder:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unique_toolkit
3
- Version: 1.11.4
3
+ Version: 1.12.1
4
4
  Summary:
5
5
  License: Proprietary
6
6
  Author: Cedric Klinkert
@@ -118,11 +118,17 @@ All notable changes to this project will be documented in this file.
118
118
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
119
119
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
120
120
 
121
+ ## [1.12.1] - 2025-10-07
122
+ - Fix bug where failed evaluations did not show an error to the user.
121
123
 
122
- ## [1.11.4] - 2026-10-06
124
+ ## [1.12.0] - 2026-10-07
125
+ - Add the `OpenAIUserMessageBuilder` for complex user messages with images
126
+ - More examples with documents/images on the chat
127
+
128
+ ## [1.11.4] - 2026-10-07
123
129
  - Make newer `MessageExecution` and `MessageLog` method keyword only
124
130
 
125
- ## [1.11.3] - 2026-10-06
131
+ ## [1.11.3] - 2026-10-07
126
132
  - Move smart rules to content
127
133
  - Add to documentation
128
134
 
@@ -31,10 +31,10 @@ unique_toolkit/agentic/evaluation/config.py,sha256=Fer-y1aP8kmnPWXQydh12i9f_XU7K
31
31
  unique_toolkit/agentic/evaluation/context_relevancy/prompts.py,sha256=EdHFUOB581yVxcOL8482KUv_LzaRjuiem71EF8udYMc,1331
32
32
  unique_toolkit/agentic/evaluation/context_relevancy/schema.py,sha256=lZd0TPzH43ifgWWGg3WO6b1AQX8aK2R9y51yH0d1DHM,2919
33
33
  unique_toolkit/agentic/evaluation/context_relevancy/service.py,sha256=fkPGq4Nnn5las1waYDICqHl6xC-rR5iOpT24YifGO20,9654
34
- unique_toolkit/agentic/evaluation/evaluation_manager.py,sha256=lh4CPYKesS7HIGVe6n-K4NMF1UI9BptNHG87W2RBpdE,7929
34
+ unique_toolkit/agentic/evaluation/evaluation_manager.py,sha256=IPx4BVUgkjFOP1BGLi0BlB6UujpXlZ0KGuSXDRemQhY,8143
35
35
  unique_toolkit/agentic/evaluation/exception.py,sha256=7lcVbCyoN4Md1chNJDFxpUYyWbVrcr9dcc3TxWykJTc,115
36
36
  unique_toolkit/agentic/evaluation/hallucination/constants.py,sha256=0HyvI5zu7JmjHLe9lKJSeAWMvfQfpmR6MLHJ4HPX1hc,2063
37
- unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py,sha256=QA0XcLmYNrExRDZA1os-1cNLQm9TyiRGzKXVLstOqW8,3141
37
+ unique_toolkit/agentic/evaluation/hallucination/hallucination_evaluation.py,sha256=yMcfA7iMNXkneNrFxJuoDIoB37mK8IRXEKnPsK_UDOk,3454
38
38
  unique_toolkit/agentic/evaluation/hallucination/prompts.py,sha256=O3Hi_rOzZlujvnO2wn2jhoPmrYLjzVtRWwxn5Q81m9Y,3405
39
39
  unique_toolkit/agentic/evaluation/hallucination/service.py,sha256=Ut-f768HY4E9zEhfMoKYnGTFRZVkxWGiSTGOpgfZWYM,2447
40
40
  unique_toolkit/agentic/evaluation/hallucination/utils.py,sha256=QLsYvgAyQ5XnKEzn7ko7bXfzePD4De99TWnMKglMpds,8178
@@ -128,7 +128,7 @@ unique_toolkit/framework_utilities/langchain/client.py,sha256=9LDRS2l9XGxL0HoFLh
128
128
  unique_toolkit/framework_utilities/langchain/history.py,sha256=R9RuCeSFNaUO3OZ0G_LmIC4gmOCIANcl91MfyWLnZ1c,650
129
129
  unique_toolkit/framework_utilities/openai/__init__.py,sha256=CrHYoC7lv2pBscitLerAFweqy5jh1R671LA_jZQ4lWo,232
130
130
  unique_toolkit/framework_utilities/openai/client.py,sha256=ct1cqPcIK1wPl11G9sJV39ZnLJwKr2kDUDSra0FjvtM,2007
131
- unique_toolkit/framework_utilities/openai/message_builder.py,sha256=VU6mJm_upLcarJQKFft_t1RlLRncWDxDuLC5LIJ5lQQ,4339
131
+ unique_toolkit/framework_utilities/openai/message_builder.py,sha256=RT1pZjxH42TFZlAxQ5zlqdKPvHKVTjc5t3JDUy58I7Q,6887
132
132
  unique_toolkit/framework_utilities/utils.py,sha256=JK7g2yMfEx3eMprug26769xqNpS5WJcizf8n2zWMBng,789
133
133
  unique_toolkit/knowledge_base.py,sha256=SHAFs68zDQuHJZdrFcdU7wnkUdfNQlNzpSLNckx3Scg,20167
134
134
  unique_toolkit/language_model/__init__.py,sha256=lRQyLlbwHbNFf4-0foBU13UGb09lwEeodbVsfsSgaCk,1971
@@ -149,7 +149,7 @@ unique_toolkit/short_term_memory/schemas.py,sha256=OhfcXyF6ACdwIXW45sKzjtZX_gkcJ
149
149
  unique_toolkit/short_term_memory/service.py,sha256=5PeVBu1ZCAfyDb2HLVvlmqSbyzBBuE9sI2o9Aajqjxg,8884
150
150
  unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
151
151
  unique_toolkit/smart_rules/compile.py,sha256=Ozhh70qCn2yOzRWr9d8WmJeTo7AQurwd3tStgBMPFLA,1246
152
- unique_toolkit-1.11.4.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
153
- unique_toolkit-1.11.4.dist-info/METADATA,sha256=nhVP6zWNPT1ZyPLFs7aTVRklPbTFIcXNmMBMGPwsUA4,35846
154
- unique_toolkit-1.11.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
155
- unique_toolkit-1.11.4.dist-info/RECORD,,
152
+ unique_toolkit-1.12.1.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
153
+ unique_toolkit-1.12.1.dist-info/METADATA,sha256=mBeCg71Dak88SQUNJpaiQwTuh9MuKmA9vDPaakm3AQg,36092
154
+ unique_toolkit-1.12.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
155
+ unique_toolkit-1.12.1.dist-info/RECORD,,