Skip to content

Commit b370ec5

Browse files
committed
fix(converters): replace "Continue" and "(empty)" with "(empty placeholder)" (#171)
1 parent 6544d1f commit b370ec5

4 files changed

Lines changed: 57 additions & 58 deletions

File tree

kiro/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ def _warn_timeout_configuration():
555555
# Application Version
556556
# ==================================================================================================
557557

558-
APP_VERSION: str = "2.4"
558+
APP_VERSION: str = "2.4.dev.13"
559559
APP_TITLE: str = "Kiro Gateway"
560560
APP_DESCRIPTION: str = "Proxy gateway for Kiro API (Amazon Q Developer / AWS CodeWhisperer). OpenAI and Anthropic compatible. Made by @jwadow"
561561

kiro/converters_core.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,7 @@ def strip_all_tool_content(messages: List[UnifiedMessage]) -> Tuple[List[Unified
965965
content_parts.append(result_text)
966966

967967
# Join all parts with double newline
968-
content = "\n\n".join(content_parts) if content_parts else "(empty)"
968+
content = "\n\n".join(content_parts) if content_parts else "(empty placeholder)"
969969

970970
# Create a copy of the message without tool content but with text representation
971971
# IMPORTANT: Preserve images from the original message (e.g., screenshots from MCP tools)
@@ -1179,7 +1179,7 @@ def ensure_first_message_is_user(messages: List[UnifiedMessage]) -> List[Unified
11791179
>>> result[0].role
11801180
'user'
11811181
>>> result[0].content
1182-
'(empty)'
1182+
'(empty placeholder)'
11831183
"""
11841184
if not messages:
11851185
return messages
@@ -1189,12 +1189,11 @@ def ensure_first_message_is_user(messages: List[UnifiedMessage]) -> List[Unified
11891189
f"First message is '{messages[0].role}', prepending synthetic user message "
11901190
f"(Kiro API requires conversations to start with user)"
11911191
)
1192-
11931192
# Create minimal synthetic user message (matches LiteLLM behavior)
1194-
# Using "(empty)" as minimal valid content to avoid disrupting conversation context
1193+
# Using "(empty placeholder)" as minimal valid content to avoid disrupting conversation context
11951194
synthetic_user = UnifiedMessage(
11961195
role="user",
1197-
content="(empty)"
1196+
content="(empty placeholder)"
11981197
)
11991198

12001199
return [synthetic_user] + messages
@@ -1263,7 +1262,7 @@ def ensure_alternating_roles(messages: List[UnifiedMessage]) -> List[UnifiedMess
12631262
12641263
Kiro API requires alternating userInputMessage and assistantResponseMessage.
12651264
When consecutive user messages are detected, synthetic assistant messages
1266-
with "(empty)" placeholder are inserted between them to maintain alternation.
1265+
with "(empty placeholder)" placeholder are inserted between them to maintain alternation.
12671266
12681267
This fixes multiple unknown roles (converted to user)
12691268
create consecutive userInputMessage entries that violate Kiro API requirements.
@@ -1286,7 +1285,7 @@ def ensure_alternating_roles(messages: List[UnifiedMessage]) -> List[UnifiedMess
12861285
>>> result[1].role
12871286
'assistant'
12881287
>>> result[1].content
1289-
'(empty)'
1288+
'(empty placeholder)'
12901289
"""
12911290
if not messages or len(messages) < 2:
12921291
return messages
@@ -1301,7 +1300,7 @@ def ensure_alternating_roles(messages: List[UnifiedMessage]) -> List[UnifiedMess
13011300
if msg.role == "user" and prev_role == "user":
13021301
synthetic_assistant = UnifiedMessage(
13031302
role="assistant",
1304-
content="(empty)" # Consistent with build_kiro_history() placeholder
1303+
content="(empty placeholder)" # Consistent with build_kiro_history() placeholder
13051304
)
13061305
result.append(synthetic_assistant)
13071306
synthetic_count += 1
@@ -1343,7 +1342,7 @@ def build_kiro_history(messages: List[UnifiedMessage], model_id: str) -> List[Di
13431342

13441343
# Fallback for empty content - Kiro API requires non-empty content
13451344
if not content:
1346-
content = "(empty)"
1345+
content = "(empty placeholder)"
13471346

13481347
user_input = {
13491348
"content": content,
@@ -1385,7 +1384,7 @@ def build_kiro_history(messages: List[UnifiedMessage], model_id: str) -> List[Di
13851384

13861385
# Fallback for empty content - Kiro API requires non-empty content
13871386
if not content:
1388-
content = "(empty)"
1387+
content = "(empty placeholder)"
13891388

13901389
assistant_response = {"content": content}
13911390

@@ -1504,18 +1503,18 @@ def build_kiro_payload(
15041503
current_content = f"{full_system_prompt}\n\n{current_content}"
15051504

15061505
# If current message is assistant, need to add it to history
1507-
# and create user message "Continue"
1506+
# and create user message placeholder
15081507
if current_message.role == "assistant":
15091508
history.append({
15101509
"assistantResponseMessage": {
15111510
"content": current_content
15121511
}
15131512
})
1514-
current_content = "Continue"
1513+
current_content = "(empty placeholder)"
15151514

1516-
# If content is empty - use "Continue"
1515+
# If content is empty - use placeholder
15171516
if not current_content:
1518-
current_content = "Continue"
1517+
current_content = "(empty placeholder)"
15191518

15201519
# Process images in current message - extract from message or content
15211520
# IMPORTANT: images go directly into userInputMessage, NOT into userInputMessageContext

tests/unit/test_converters_core.py

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,7 @@ def test_prepends_synthetic_user_when_first_is_assistant(self):
13421342

13431343
print("Checking first message is synthetic user...")
13441344
assert result[0].role == "user"
1345-
assert result[0].content == "(empty)"
1345+
assert result[0].content == "(empty placeholder)"
13461346

13471347
print("Checking original messages are preserved...")
13481348
assert result[1].role == "assistant"
@@ -1379,7 +1379,7 @@ def test_handles_single_assistant_message(self):
13791379
print(f"Comparing length: Expected 2 (synthetic + original), Got {len(result)}")
13801380
assert len(result) == 2
13811381
assert result[0].role == "user"
1382-
assert result[0].content == "(empty)"
1382+
assert result[0].content == "(empty placeholder)"
13831383
assert result[1].role == "assistant"
13841384

13851385
def test_handles_assistant_user_assistant_sequence(self):
@@ -1400,7 +1400,7 @@ def test_handles_assistant_user_assistant_sequence(self):
14001400
print(f"Comparing length: Expected 4 (synthetic + 3 original), Got {len(result)}")
14011401
assert len(result) == 4
14021402
assert result[0].role == "user"
1403-
assert result[0].content == "(empty)"
1403+
assert result[0].content == "(empty placeholder)"
14041404
assert result[1].role == "assistant"
14051405
assert result[2].role == "user"
14061406
assert result[3].role == "assistant"
@@ -1429,7 +1429,7 @@ def test_preserves_tool_calls_in_assistant_message(self):
14291429
print("Checking synthetic user was prepended...")
14301430
assert len(result) == 2
14311431
assert result[0].role == "user"
1432-
assert result[0].content == "(empty)"
1432+
assert result[0].content == "(empty placeholder)"
14331433

14341434
print("Checking tool_calls are preserved...")
14351435
assert result[1].role == "assistant"
@@ -1463,7 +1463,7 @@ def test_preserves_images_in_messages(self):
14631463

14641464
def test_uses_minimal_content_for_synthetic_message(self):
14651465
"""
1466-
What it does: Verifies synthetic message uses minimal content ("(empty)").
1466+
What it does: Verifies synthetic message uses minimal content ("(empty placeholder)").
14671467
Purpose: Ensure minimal token usage and avoid disrupting conversation context.
14681468
"""
14691469
print("Setup: Assistant-first conversation...")
@@ -1475,7 +1475,7 @@ def test_uses_minimal_content_for_synthetic_message(self):
14751475
result = ensure_first_message_is_user(messages)
14761476

14771477
print("Checking synthetic message content...")
1478-
assert result[0].content == "(empty)"
1478+
assert result[0].content == "(empty placeholder)"
14791479
print("✓ Synthetic message uses minimal content (matches LiteLLM behavior)")
14801480

14811481

@@ -1685,7 +1685,7 @@ class TestEnsureAlternatingRoles:
16851685
Tests for ensure_alternating_roles function.
16861686
16871687
This function ensures alternating user/assistant roles by inserting synthetic
1688-
assistant messages with "(empty)" content between consecutive user messages.
1688+
assistant messages with "(empty placeholder)" content between consecutive user messages.
16891689
This is part of the fix for Issue #64 where multiple 'developer' roles
16901690
(converted to 'user') create consecutive userInputMessage entries.
16911691
"""
@@ -1710,7 +1710,7 @@ def test_inserts_synthetic_assistant_between_two_consecutive_users(self):
17101710
assert result[0].role == "user"
17111711
assert result[0].content == "First"
17121712
assert result[1].role == "assistant"
1713-
assert result[1].content == "(empty)"
1713+
assert result[1].content == "(empty placeholder)"
17141714
assert result[2].role == "user"
17151715
assert result[2].content == "Second"
17161716

@@ -1734,11 +1734,11 @@ def test_inserts_multiple_synthetic_assistants_for_four_consecutive_users(self):
17341734
assert len(result) == 7
17351735
print("Checking alternation pattern...")
17361736
assert result[0].role == "user" and result[0].content == "First"
1737-
assert result[1].role == "assistant" and result[1].content == "(empty)"
1737+
assert result[1].role == "assistant" and result[1].content == "(empty placeholder)"
17381738
assert result[2].role == "user" and result[2].content == "Second"
1739-
assert result[3].role == "assistant" and result[3].content == "(empty)"
1739+
assert result[3].role == "assistant" and result[3].content == "(empty placeholder)"
17401740
assert result[4].role == "user" and result[4].content == "Third"
1741-
assert result[5].role == "assistant" and result[5].content == "(empty)"
1741+
assert result[5].role == "assistant" and result[5].content == "(empty placeholder)"
17421742
assert result[6].role == "user" and result[6].content == "Fourth"
17431743

17441744
def test_preserves_already_alternating_messages(self):
@@ -1787,15 +1787,15 @@ def test_handles_multiple_groups_of_consecutive_users(self):
17871787
assert len(result) == 9
17881788
print("Checking first group (A, synthetic, B)...")
17891789
assert result[0].role == "user" and result[0].content == "A"
1790-
assert result[1].role == "assistant" and result[1].content == "(empty)"
1790+
assert result[1].role == "assistant" and result[1].content == "(empty placeholder)"
17911791
assert result[2].role == "user" and result[2].content == "B"
17921792
print("Checking real assistant...")
17931793
assert result[3].role == "assistant" and result[3].content == "C"
17941794
print("Checking second group (D, synthetic, E, synthetic, F)...")
17951795
assert result[4].role == "user" and result[4].content == "D"
1796-
assert result[5].role == "assistant" and result[5].content == "(empty)"
1796+
assert result[5].role == "assistant" and result[5].content == "(empty placeholder)"
17971797
assert result[6].role == "user" and result[6].content == "E"
1798-
assert result[7].role == "assistant" and result[7].content == "(empty)"
1798+
assert result[7].role == "assistant" and result[7].content == "(empty placeholder)"
17991799
assert result[8].role == "user" and result[8].content == "F"
18001800

18011801
def test_handles_empty_list(self):
@@ -1931,11 +1931,11 @@ def test_developer_messages_are_normalized_and_alternated(self):
19311931
assert len(result) == 7
19321932
print("Checking alternation pattern...")
19331933
assert result[0].role == "user" and result[0].content == "Context 1"
1934-
assert result[1].role == "assistant" and result[1].content == "(empty)"
1934+
assert result[1].role == "assistant" and result[1].content == "(empty placeholder)"
19351935
assert result[2].role == "user" and result[2].content == "Context 2"
1936-
assert result[3].role == "assistant" and result[3].content == "(empty)"
1936+
assert result[3].role == "assistant" and result[3].content == "(empty placeholder)"
19371937
assert result[4].role == "user" and result[4].content == "Context 3"
1938-
assert result[5].role == "assistant" and result[5].content == "(empty)"
1938+
assert result[5].role == "assistant" and result[5].content == "(empty placeholder)"
19391939
assert result[6].role == "user" and result[6].content == "Question"
19401940

19411941
def test_mixed_roles_are_normalized_and_alternated(self):
@@ -1970,13 +1970,13 @@ def test_mixed_roles_are_normalized_and_alternated(self):
19701970
assert len(result) == 9
19711971
print("Checking that all system/developer were converted to user...")
19721972
assert result[0].role == "user" and result[0].content == "System"
1973-
assert result[1].role == "assistant" and result[1].content == "(empty)"
1973+
assert result[1].role == "assistant" and result[1].content == "(empty placeholder)"
19741974
assert result[2].role == "user" and result[2].content == "Dev"
1975-
assert result[3].role == "assistant" and result[3].content == "(empty)"
1975+
assert result[3].role == "assistant" and result[3].content == "(empty placeholder)"
19761976
assert result[4].role == "user" and result[4].content == "User1"
19771977
assert result[5].role == "assistant" and result[5].content == "Assistant1"
19781978
assert result[6].role == "user" and result[6].content == "Dev2"
1979-
assert result[7].role == "assistant" and result[7].content == "(empty)"
1979+
assert result[7].role == "assistant" and result[7].content == "(empty placeholder)"
19801980
assert result[8].role == "user" and result[8].content == "User2"
19811981

19821982

@@ -3899,7 +3899,7 @@ def test_builds_assistant_message_with_tool_calls(self):
38993899

39003900
def test_adds_empty_placeholder_for_empty_user_content(self):
39013901
"""
3902-
What it does: Verifies that "(empty)" placeholder is added for user messages with empty content.
3902+
What it does: Verifies that "(empty placeholder)" placeholder is added for user messages with empty content.
39033903
Purpose: Ensure Kiro API receives non-empty content in history.
39043904
39053905
This is a fallback test for issue #20 - ensures any edge case with empty content
@@ -3913,12 +3913,12 @@ def test_adds_empty_placeholder_for_empty_user_content(self):
39133913

39143914
print(f"Result: {result}")
39153915
print(f"Content: '{result[0]['userInputMessage']['content']}'")
3916-
print("Checking that '(empty)' placeholder is added...")
3917-
assert result[0]["userInputMessage"]["content"] == "(empty)"
3916+
print("Checking that '(empty placeholder)' placeholder is added...")
3917+
assert result[0]["userInputMessage"]["content"] == "(empty placeholder)"
39183918

39193919
def test_adds_empty_placeholder_for_empty_assistant_content(self):
39203920
"""
3921-
What it does: Verifies that "(empty)" placeholder is added for assistant messages with empty content.
3921+
What it does: Verifies that "(empty placeholder)" placeholder is added for assistant messages with empty content.
39223922
Purpose: Ensure Kiro API receives non-empty content in history.
39233923
39243924
This is a fallback test for issue #20 - ensures any edge case with empty content
@@ -3932,12 +3932,12 @@ def test_adds_empty_placeholder_for_empty_assistant_content(self):
39323932

39333933
print(f"Result: {result}")
39343934
print(f"Content: '{result[0]['assistantResponseMessage']['content']}'")
3935-
print("Checking that '(empty)' placeholder is added...")
3936-
assert result[0]["assistantResponseMessage"]["content"] == "(empty)"
3935+
print("Checking that '(empty placeholder)' placeholder is added...")
3936+
assert result[0]["assistantResponseMessage"]["content"] == "(empty placeholder)"
39373937

39383938
def test_adds_empty_placeholder_for_none_user_content(self):
39393939
"""
3940-
What it does: Verifies that "(empty)" placeholder is added for user messages with None content.
3940+
What it does: Verifies that "(empty placeholder)" placeholder is added for user messages with None content.
39413941
Purpose: Ensure Kiro API receives non-empty content when content is None.
39423942
"""
39433943
print("Setup: User message with None content...")
@@ -3948,12 +3948,12 @@ def test_adds_empty_placeholder_for_none_user_content(self):
39483948

39493949
print(f"Result: {result}")
39503950
print(f"Content: '{result[0]['userInputMessage']['content']}'")
3951-
print("Checking that '(empty)' placeholder is added...")
3952-
assert result[0]["userInputMessage"]["content"] == "(empty)"
3951+
print("Checking that '(empty placeholder)' placeholder is added...")
3952+
assert result[0]["userInputMessage"]["content"] == "(empty placeholder)"
39533953

39543954
def test_adds_empty_placeholder_for_none_assistant_content(self):
39553955
"""
3956-
What it does: Verifies that "(empty)" placeholder is added for assistant messages with None content.
3956+
What it does: Verifies that "(empty placeholder)" placeholder is added for assistant messages with None content.
39573957
Purpose: Ensure Kiro API receives non-empty content when content is None.
39583958
"""
39593959
print("Setup: Assistant message with None content...")
@@ -3964,8 +3964,8 @@ def test_adds_empty_placeholder_for_none_assistant_content(self):
39643964

39653965
print(f"Result: {result}")
39663966
print(f"Content: '{result[0]['assistantResponseMessage']['content']}'")
3967-
print("Checking that '(empty)' placeholder is added...")
3968-
assert result[0]["assistantResponseMessage"]["content"] == "(empty)"
3967+
print("Checking that '(empty placeholder)' placeholder is added...")
3968+
assert result[0]["assistantResponseMessage"]["content"] == "(empty placeholder)"
39693969

39703970
def test_preserves_non_empty_content_in_history(self):
39713971
"""
@@ -4011,10 +4011,10 @@ def test_mixed_empty_and_non_empty_content_in_history(self):
40114011
assert result[0]["userInputMessage"]["content"] == "Start"
40124012

40134013
print(f"Message 1 content: '{result[1]['assistantResponseMessage']['content']}'")
4014-
assert result[1]["assistantResponseMessage"]["content"] == "(empty)"
4014+
assert result[1]["assistantResponseMessage"]["content"] == "(empty placeholder)"
40154015

40164016
print(f"Message 2 content: '{result[2]['userInputMessage']['content']}'")
4017-
assert result[2]["userInputMessage"]["content"] == "(empty)"
4017+
assert result[2]["userInputMessage"]["content"] == "(empty placeholder)"
40184018

40194019
print(f"Message 3 content: '{result[3]['assistantResponseMessage']['content']}'")
40204020
assert result[3]["assistantResponseMessage"]["content"] == "Response"
@@ -4488,7 +4488,7 @@ def test_mixed_messages_with_and_without_tool_content(self):
44884488
content="",
44894489
tool_calls=[{"id": "call_1", "type": "function", "function": {"name": "tool", "arguments": "{}"}}]
44904490
), # Has tool content
4491-
UnifiedMessage(role="user", content="Continue"), # No tool content
4491+
UnifiedMessage(role="user", content="(empty placeholder)"), # No tool content
44924492
]
44934493

44944494
print("Action: Stripping tool content...")
@@ -4499,7 +4499,7 @@ def test_mixed_messages_with_and_without_tool_content(self):
44994499
assert result[0].content == "Hello"
45004500
assert result[0].tool_calls is None
45014501
assert result[1].tool_calls is None # Stripped
4502-
assert result[2].content == "Continue"
4502+
assert result[2].content == "(empty placeholder)"
45034503
assert result[2].tool_calls is None
45044504
assert had_content is True
45054505

@@ -5507,7 +5507,7 @@ def test_with_tools_defined_keeps_tool_structure(self):
55075507
"content": "Tool executed"
55085508
}]
55095509
),
5510-
UnifiedMessage(role="user", content="Continue")
5510+
UnifiedMessage(role="user", content="(empty placeholder)")
55115511
]
55125512

55135513
tools = [UnifiedTool(
@@ -5558,7 +5558,7 @@ def test_empty_tools_list_triggers_stripping(self):
55585558
"function": {"name": "some_tool", "arguments": "{}"}
55595559
}]
55605560
),
5561-
UnifiedMessage(role="user", content="Continue")
5561+
UnifiedMessage(role="user", content="(empty placeholder)")
55625562
]
55635563

55645564
print("Action: Building Kiro payload with empty tools list...")

0 commit comments

Comments
 (0)