unique_toolkit 2026.40.0.dev1__py3-none-any.whl → 2026.40.0.dev3__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.
- unique_toolkit/agentic/evaluation/hallucination/utils.py +3 -2
- unique_toolkit/agentic/evaluation/tests/test_hallucination_utils.py +64 -0
- unique_toolkit/chat/functions.py +40 -0
- unique_toolkit/chat/schemas.py +6 -0
- unique_toolkit/services/chat_service.py +31 -0
- {unique_toolkit-2026.40.0.dev1.dist-info → unique_toolkit-2026.40.0.dev3.dist-info}/METADATA +2 -2
- {unique_toolkit-2026.40.0.dev1.dist-info → unique_toolkit-2026.40.0.dev3.dist-info}/RECORD +8 -8
- {unique_toolkit-2026.40.0.dev1.dist-info → unique_toolkit-2026.40.0.dev3.dist-info}/WHEEL +0 -0
|
@@ -203,7 +203,8 @@ def _compose_msgs(
|
|
|
203
203
|
)
|
|
204
204
|
system_msg = LanguageModelSystemMessage(content=system_msg_content)
|
|
205
205
|
|
|
206
|
-
# Render user message
|
|
206
|
+
# Render user message. Pass history text only when history exists so the
|
|
207
|
+
# template can omit an empty conversation section.
|
|
207
208
|
user_msg_content = render_template(
|
|
208
209
|
user_template,
|
|
209
210
|
input_text=input.input_text,
|
|
@@ -211,7 +212,7 @@ def _compose_msgs(
|
|
|
211
212
|
if has_context
|
|
212
213
|
else None,
|
|
213
214
|
history_messages_text=input.get_joined_history_texts(tag_name="conversation")
|
|
214
|
-
if has_context
|
|
215
|
+
if has_context and input.history_messages
|
|
215
216
|
else None,
|
|
216
217
|
output_text=input.output_text,
|
|
217
218
|
)
|
|
@@ -1224,3 +1224,67 @@ def test_get_msgs__ignores_context_images__when_model_lacks_vision() -> None:
|
|
|
1224
1224
|
# Assert
|
|
1225
1225
|
assert result.root[0].content == no_context_result.root[0].content
|
|
1226
1226
|
assert isinstance(result.root[1].content, str)
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
@pytest.mark.unit
|
|
1230
|
+
def test_compose_msgs__omits_conversation_section__when_history_is_empty():
|
|
1231
|
+
"""A single-turn evaluation must not be shown an empty conversation.
|
|
1232
|
+
|
|
1233
|
+
The template guards the section with `{% if history_messages_text %}`,
|
|
1234
|
+
and the getter returns a truthy placeholder when there is no history, so
|
|
1235
|
+
the guard used to never fire: every caller evaluating one turn shipped a
|
|
1236
|
+
`Conversation:` section whose body said there was no conversation, while
|
|
1237
|
+
the system prompt announced one.
|
|
1238
|
+
"""
|
|
1239
|
+
msgs = _compose_msgs(
|
|
1240
|
+
EvaluationMetricInput(
|
|
1241
|
+
input_text="What is the policy rate?",
|
|
1242
|
+
context_texts=["The policy rate is 0.25%."],
|
|
1243
|
+
history_messages=[],
|
|
1244
|
+
output_text="0.25%.",
|
|
1245
|
+
),
|
|
1246
|
+
HallucinationConfig(),
|
|
1247
|
+
has_context=True,
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
user_content = msgs.root[-1].content
|
|
1251
|
+
assert isinstance(user_content, str)
|
|
1252
|
+
assert "Conversation:" not in user_content
|
|
1253
|
+
assert "<No conversation texts provided>" not in user_content
|
|
1254
|
+
# The references the model does have must still be there.
|
|
1255
|
+
assert "The policy rate is 0.25%." in user_content
|
|
1256
|
+
|
|
1257
|
+
|
|
1258
|
+
@pytest.mark.unit
|
|
1259
|
+
def test_compose_msgs__keeps_conversation_section__when_history_is_present():
|
|
1260
|
+
msgs = _compose_msgs(
|
|
1261
|
+
EvaluationMetricInput(
|
|
1262
|
+
input_text="And in March?",
|
|
1263
|
+
context_texts=["The March rate was 0.50%."],
|
|
1264
|
+
history_messages=[
|
|
1265
|
+
ChatMessage(
|
|
1266
|
+
id="msg_1",
|
|
1267
|
+
chat_id="chat_1",
|
|
1268
|
+
previous_message_id=None,
|
|
1269
|
+
role=LanguageModelMessageRole.USER,
|
|
1270
|
+
text="What is the policy rate?",
|
|
1271
|
+
),
|
|
1272
|
+
ChatMessage(
|
|
1273
|
+
id="msg_2",
|
|
1274
|
+
chat_id="chat_1",
|
|
1275
|
+
previous_message_id="msg_1",
|
|
1276
|
+
role=LanguageModelMessageRole.ASSISTANT,
|
|
1277
|
+
text="0.25%.",
|
|
1278
|
+
),
|
|
1279
|
+
],
|
|
1280
|
+
output_text="0.50%.",
|
|
1281
|
+
),
|
|
1282
|
+
HallucinationConfig(),
|
|
1283
|
+
has_context=True,
|
|
1284
|
+
)
|
|
1285
|
+
|
|
1286
|
+
user_content = msgs.root[-1].content
|
|
1287
|
+
assert isinstance(user_content, str)
|
|
1288
|
+
assert "Conversation:" in user_content
|
|
1289
|
+
assert "What is the policy rate?" in user_content
|
|
1290
|
+
assert "<No conversation texts provided>" not in user_content
|
unique_toolkit/chat/functions.py
CHANGED
|
@@ -188,6 +188,46 @@ async def modify_message_async(
|
|
|
188
188
|
raise e
|
|
189
189
|
|
|
190
190
|
|
|
191
|
+
async def mark_turn_system_interrupted_async(
|
|
192
|
+
user_id: str,
|
|
193
|
+
company_id: str,
|
|
194
|
+
chat_id: str,
|
|
195
|
+
user_message_id: str,
|
|
196
|
+
) -> ChatMessage:
|
|
197
|
+
"""Marks the turn anchored on *user_message_id* as system-interrupted.
|
|
198
|
+
|
|
199
|
+
Persists ``turnInterruptionReason = SYSTEM_INTERRUPTED`` on the originating
|
|
200
|
+
USER message via a dedicated service-authenticated node-chat endpoint. The
|
|
201
|
+
server guarantees idempotency and never overwrites ``userAbortedAt`` (a
|
|
202
|
+
user-initiated stop wins if both race).
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
user_id (str): The user ID.
|
|
206
|
+
company_id (str): The company ID.
|
|
207
|
+
chat_id (str): The chat ID.
|
|
208
|
+
user_message_id (str): The originating USER message of the turn.
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
ChatMessage: The USER message after the update (unchanged if the call
|
|
212
|
+
was a no-op due to idempotency or a racing user stop).
|
|
213
|
+
|
|
214
|
+
Raises:
|
|
215
|
+
Exception: If the request fails.
|
|
216
|
+
|
|
217
|
+
"""
|
|
218
|
+
try:
|
|
219
|
+
message = await unique_sdk.Message.mark_turn_system_interrupted_async(
|
|
220
|
+
user_id=user_id,
|
|
221
|
+
company_id=company_id,
|
|
222
|
+
id=user_message_id,
|
|
223
|
+
chatId=chat_id,
|
|
224
|
+
)
|
|
225
|
+
return ChatMessage(**message)
|
|
226
|
+
except Exception as e:
|
|
227
|
+
_LOGGER.error(f"Failed to mark turn as system-interrupted: {e}")
|
|
228
|
+
raise e
|
|
229
|
+
|
|
230
|
+
|
|
191
231
|
def map_references(references: list[ContentReference]) -> list[dict[str, Any]]:
|
|
192
232
|
return [
|
|
193
233
|
{
|
unique_toolkit/chat/schemas.py
CHANGED
|
@@ -192,6 +192,12 @@ class MessageLogStatus(StrEnum):
|
|
|
192
192
|
RUNNING = "RUNNING"
|
|
193
193
|
COMPLETED = "COMPLETED"
|
|
194
194
|
FAILED = "FAILED"
|
|
195
|
+
CANCELLED = "CANCELLED"
|
|
196
|
+
"""The user pressed Stop while this thinking/tool step was in flight.
|
|
197
|
+
|
|
198
|
+
Use ``FAILED`` for system-side interruption (timeout, truncation, terminal
|
|
199
|
+
backend failure), not for user-initiated cancellation.
|
|
200
|
+
"""
|
|
195
201
|
|
|
196
202
|
|
|
197
203
|
class MessageExecutionStatus(StrEnum):
|
|
@@ -49,6 +49,7 @@ from unique_toolkit.chat.functions import (
|
|
|
49
49
|
get_message_tools,
|
|
50
50
|
get_message_tools_async,
|
|
51
51
|
get_selection_from_history,
|
|
52
|
+
mark_turn_system_interrupted_async,
|
|
52
53
|
modify_message,
|
|
53
54
|
modify_message_assessment,
|
|
54
55
|
modify_message_assessment_async,
|
|
@@ -383,6 +384,36 @@ class ChatService(ChatServiceDeprecated):
|
|
|
383
384
|
set_completed_at=set_completed_at or False,
|
|
384
385
|
)
|
|
385
386
|
|
|
387
|
+
async def mark_turn_system_interrupted_async(self) -> ChatMessage:
|
|
388
|
+
"""Marks the current turn as system-interrupted.
|
|
389
|
+
|
|
390
|
+
Persists ``turnInterruptionReason = SYSTEM_INTERRUPTED`` on the current
|
|
391
|
+
chat context's originating USER message via a dedicated
|
|
392
|
+
service-authenticated node-chat endpoint.
|
|
393
|
+
|
|
394
|
+
Call this only for system-side unexpected endings: timeout, truncation,
|
|
395
|
+
terminal backend failure, and equivalents. Do not call it for
|
|
396
|
+
user-initiated cancellation or normal completion.
|
|
397
|
+
|
|
398
|
+
The operation is idempotent and never overwrites ``userAbortedAt``: if
|
|
399
|
+
the user pressed Stop and the system interruption race, the user stop
|
|
400
|
+
wins and this call is a server-side no-op.
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
ChatMessage: The USER message anchoring the turn.
|
|
404
|
+
|
|
405
|
+
Raises:
|
|
406
|
+
Exception: If the request fails. Callers invoking this from
|
|
407
|
+
failure-handling paths should treat it as best-effort.
|
|
408
|
+
|
|
409
|
+
"""
|
|
410
|
+
return await mark_turn_system_interrupted_async(
|
|
411
|
+
user_id=self._user_id,
|
|
412
|
+
company_id=self._company_id,
|
|
413
|
+
chat_id=self._chat_id,
|
|
414
|
+
user_message_id=self._user_message_id,
|
|
415
|
+
)
|
|
416
|
+
|
|
386
417
|
def modify_assistant_message(
|
|
387
418
|
self,
|
|
388
419
|
content: str | None = None,
|
{unique_toolkit-2026.40.0.dev1.dist-info → unique_toolkit-2026.40.0.dev3.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: unique-toolkit
|
|
3
|
-
Version: 2026.40.0.
|
|
3
|
+
Version: 2026.40.0.dev3
|
|
4
4
|
Summary:
|
|
5
5
|
Author: Cedric Klinkert, Martin Fadler, Sadique Sheik, Fabian Schläpfer, Pascal Hauri, Ejiroghene Oki-Peter, Rami Azouz, Ricardo Visini, Sanzio Monti
|
|
6
6
|
Author-email: Cedric Klinkert <cedric.klinkert@unique.ch>, Martin Fadler <martin.fadler@unique.ch>, Sadique Sheik <sadique@unique.ch>, Fabian Schläpfer <fabian@unique.ch>, Pascal Hauri <pascal@unique.ch>, Ejiroghene Oki-Peter <oki-peter.ejiroghene@unique.ai>, Rami Azouz <rami.ext@unique.ch>, Ricardo Visini <ricardo.visini@unique.ch>, Sanzio Monti <sanzio.monti@unique.ai>
|
|
@@ -16,7 +16,7 @@ Requires-Dist: sseclient>=0.0.27,<0.0.28
|
|
|
16
16
|
Requires-Dist: openai>=1.105.0,<3
|
|
17
17
|
Requires-Dist: platformdirs>=4.0.0,<5
|
|
18
18
|
Requires-Dist: pillow>=12.1.1
|
|
19
|
-
Requires-Dist: unique-sdk>=2026.40.0.
|
|
19
|
+
Requires-Dist: unique-sdk>=2026.40.0.dev3,<2026.40.0rc0
|
|
20
20
|
Requires-Dist: jambo>=0.1.2,<0.2
|
|
21
21
|
Requires-Dist: jinja2>=3.1.6,<4
|
|
22
22
|
Requires-Dist: docxtpl>=0.20.1,<0.21
|
|
@@ -99,7 +99,7 @@ unique_toolkit/agentic/evaluation/hallucination/prompts/__init__.py,sha256=4KFYM
|
|
|
99
99
|
unique_toolkit/agentic/evaluation/hallucination/prompts/system_prompt.j2,sha256=sDUX6G645Ba40D_qKu4cUI8g-sJOfG8JpZreTNFgf7M,2616
|
|
100
100
|
unique_toolkit/agentic/evaluation/hallucination/prompts/user_prompt.j2,sha256=mD_qE9fOkyc1XXrebFt097ddx8bTlA6lbY04hKSQmWs,273
|
|
101
101
|
unique_toolkit/agentic/evaluation/hallucination/service.py,sha256=QSPsRxx2H-YpSR2Jn7zJKtwZfZ7elTPAFQiYvM3gYfs,2618
|
|
102
|
-
unique_toolkit/agentic/evaluation/hallucination/utils.py,sha256=
|
|
102
|
+
unique_toolkit/agentic/evaluation/hallucination/utils.py,sha256=maiMxlUjT9_U0GAH9HvemMIUE-47MCGtwm2XMwDAShQ,15664
|
|
103
103
|
unique_toolkit/agentic/evaluation/output_parser.py,sha256=0FDo8YY_Dc4qlTNeYyQkznzIFj9aX9wMrLOTbhhTl6g,1418
|
|
104
104
|
unique_toolkit/agentic/evaluation/schemas.py,sha256=RT0O-AXSOf1zV3fDbUfn7V6bOIA01pkzui02Uad9E3Y,3815
|
|
105
105
|
unique_toolkit/agentic/evaluation/tests/fixtures.py,sha256=Q-ughTfDiAdsMKbBVGzFiBucFdAx-FXgJ9iqp5xMyPs,2801
|
|
@@ -107,7 +107,7 @@ unique_toolkit/agentic/evaluation/tests/test_config.py,sha256=p7xFQ7KE_yU8jGpqYA
|
|
|
107
107
|
unique_toolkit/agentic/evaluation/tests/test_context_relevancy_service.py,sha256=-GRxH70vzvrZG6Hz0igH6i2CqM7Z9jjKt3396xWDh84,12011
|
|
108
108
|
unique_toolkit/agentic/evaluation/tests/test_hallucination_constants.py,sha256=A77nRcniI7hUf640f1sTvCAibQbJE30ikbz1RX4fltw,20478
|
|
109
109
|
unique_toolkit/agentic/evaluation/tests/test_hallucination_external_context.py,sha256=3JR9WfBWazX7a1h3uq77Q6kSKm8Mt-aAW8KUYxJUw14,4303
|
|
110
|
-
unique_toolkit/agentic/evaluation/tests/test_hallucination_utils.py,sha256=
|
|
110
|
+
unique_toolkit/agentic/evaluation/tests/test_hallucination_utils.py,sha256=TJcJf9SZcH1dsSq38NkvSBM0VZfBRkan49Una3UPQB0,41546
|
|
111
111
|
unique_toolkit/agentic/evaluation/tests/test_output_parser.py,sha256=KfltytmvqnPWLhmZpBXqcRmnlYorw_USwM5rkLVv8so,5179
|
|
112
112
|
unique_toolkit/agentic/evaluation/tests/test_prompt_loaders.py,sha256=zBREdlKf5tdDyB8XSaNgpQv3-tuZJoYteeJrp6WMWDM,11897
|
|
113
113
|
unique_toolkit/agentic/evaluation/utils.py,sha256=HmyPaDV8wdW-_gOjjW-wDaMKgdrsP5-SHP7OqTmGI_A,264
|
|
@@ -260,10 +260,10 @@ unique_toolkit/chat/cancellation.py,sha256=k5HMjpeBmofIiInwq5TjOR_5k91oq1bWG00lJ
|
|
|
260
260
|
unique_toolkit/chat/constants.py,sha256=05kq6zjqUVB2d6_P7s-90nbljpB3ryxwCI-CAz0r2O4,83
|
|
261
261
|
unique_toolkit/chat/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
262
262
|
unique_toolkit/chat/deprecated/service.py,sha256=8_XoopUWHT396Z_5gwMmsI6v4NGVgQirSJCYUW4Gx8c,9339
|
|
263
|
-
unique_toolkit/chat/functions.py,sha256=
|
|
263
|
+
unique_toolkit/chat/functions.py,sha256=fb4x_y4d5QwT1WUjVEP-opFpJ9CV8WaiLAOR1ktTmy0,57073
|
|
264
264
|
unique_toolkit/chat/rendering.py,sha256=c8YiV9oADRrJQ5A_QBJ4_UFc0NZ-2vVaa7tupoMusso,880
|
|
265
265
|
unique_toolkit/chat/responses_api.py,sha256=Swp6wi10vCPkBiSSj8NN58gpjTgYVkqRlwdOJRQHVjo,21135
|
|
266
|
-
unique_toolkit/chat/schemas.py,sha256=
|
|
266
|
+
unique_toolkit/chat/schemas.py,sha256=tRU8vcT1SQWtktkHQmDO26CNbOgSynFHfFB9FR6D4OQ,8372
|
|
267
267
|
unique_toolkit/chat/service.py,sha256=6D00OL4QrGafbOhTaC5zNXaNgg7gS5W_2ePVa4LhqpE,4439
|
|
268
268
|
unique_toolkit/chat/state.py,sha256=Cjgwv_2vhDFbV69xxsn7SefhaoIAEqLx3ferdVFCnOg,1445
|
|
269
269
|
unique_toolkit/chat/utils.py,sha256=CsD9IER2gA1IOV0fmUOl-N0LmKvA7JP9dGrOqeOY0Gg,1173
|
|
@@ -412,7 +412,7 @@ unique_toolkit/monitoring/registry.py,sha256=De2Bx5QBBRJQY2MM3xtntiJ2JU2FoKkVT7r
|
|
|
412
412
|
unique_toolkit/monitoring/tracing.py,sha256=0lmLuQjyyC3iFpNTw8rOyCpEZ6PTvp16nVbzcO9d2sQ,10043
|
|
413
413
|
unique_toolkit/protocols/support.py,sha256=x5edzbBmKAUuvm3oLFh52gkgQsqR5LU7M0t8RHK0DZk,5161
|
|
414
414
|
unique_toolkit/services/__init__.py,sha256=b9Wl7Wvz1JmZDbDexIZzBoXZ0wscXuczGAH8LoL-Qak,572
|
|
415
|
-
unique_toolkit/services/chat_service.py,sha256=
|
|
415
|
+
unique_toolkit/services/chat_service.py,sha256=umPHKDn0FX7vG7y7Tw-88iMultPpw_r0fYlhQlqvLYY,100196
|
|
416
416
|
unique_toolkit/services/factory.py,sha256=7gNeanyHkgX23Iz7RMCQTCkE9gI5asCI11nXxM0jjx4,9832
|
|
417
417
|
unique_toolkit/services/knowledge_base.py,sha256=6dM4abcXR9y2VYoR6SK79gwjrzb83Wm8jMPPIt5z95o,43461
|
|
418
418
|
unique_toolkit/short_term_memory/__init__.py,sha256=2mI3AUrffgH7Yt-xS57EGqnHf7jnn6xquoKEhJqk3Wg,185
|
|
@@ -423,6 +423,6 @@ unique_toolkit/short_term_memory/service.py,sha256=2JMBEBn6GoyK-a0xvdcWpPft-3C74
|
|
|
423
423
|
unique_toolkit/smart_rules/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
424
424
|
unique_toolkit/smart_rules/compile.py,sha256=vRe0aMJWuVIK-VsqomgxQImzke7MDmwgSxxJfYCa7zw,1386
|
|
425
425
|
unique_toolkit/test_utilities/events.py,sha256=FLrhMh0A_mQutAUjoD0FdMcVQ4Gq31r35OVGzRjvLro,6760
|
|
426
|
-
unique_toolkit-2026.40.0.
|
|
427
|
-
unique_toolkit-2026.40.0.
|
|
428
|
-
unique_toolkit-2026.40.0.
|
|
426
|
+
unique_toolkit-2026.40.0.dev3.dist-info/WHEEL,sha256=eh7sammvW2TypMMMGKgsM83HyA_3qQ5Lgg3ynoecH3M,79
|
|
427
|
+
unique_toolkit-2026.40.0.dev3.dist-info/METADATA,sha256=5lKBF9LaItGW_BMKf--Pm1cu_WyBA_yMPHpWd9XPrHs,7821
|
|
428
|
+
unique_toolkit-2026.40.0.dev3.dist-info/RECORD,,
|
|
File without changes
|