Task #36
๐ง Bug Fix Report: `tool_use ids without tool_result` 400 ์๋ฌ
์์ ๋ช ๋ น์ด
[๐ด HIGH] telegram_bot.py์ _chat_with_tools ํจ์์์ ๋ฐ์ํ๋ ๋ฐ๋ณต์ ์ธ 400 ์๋ฌ ("tool_use ids were found without tool_result blocks") ๋ฒ๊ทธ๋ฅผ ์์ ํ๋ผ.
## ๋ฌธ์ ๋ถ์
`_chat_with_tools` ๋ฃจํ (1677~1816๋ผ์ธ)์์ Claude API๊ฐ `web_search` (server_tool_use) ๋๊ตฌ๋ฅผ ์ฌ์ฉํ ๋ 400 ์๋ฌ๊ฐ ๋ฐ๋ณต ๋ฐ์ํ๋ค.
### ํต์ฌ ์์ธ
1. **`web_search_tool_result`๊ฐ ์ด๋ฏธ assistant content์ ํฌํจ๋๋ ์ผ์ด์ค ๋ฏธ์ฒ๋ฆฌ**
- Claude API๋ `web_search` ์ฌ์ฉ ์, `server_tool_use` ๋ธ๋ก **AND** `web_search_tool_result` ๋ธ๋ก์ **๋ชจ๋ assistant message content์ ํฌํจ**ํด์ ๋ฐํํ ์ ์์
- ํ์ฌ ์ฝ๋(1700~1714๋ผ์ธ)์์ `web_search_tool_result` ๋ธ๋ก ์ฒ๋ฆฌ:
```python
elif block.type == "web_search_tool_result":
assistant_content.append(block.model_dump())
```
- ์ด ์ผ์ด์ค๊ฐ ๋ง๊ฒ ์ฒ๋ฆฌ๋๋ ๊ฒ ๊ฐ์ง๋ง, `pending_server_ids` ์์ง ์ ์ด๋ฏธ ๊ฒฐ๊ณผ๊ฐ ํฌํจ๋ `server_tool_use` ID๊น์ง ํฌํจํด์ dummy๋ฅผ ์ค๋ณต ์ฃผ์
ํ๋ค.
2. **`pending_server_ids` ๋ก์ง ์ค๋ฅ (1750~1766๋ผ์ธ)**
```python
pending_server_ids = [
b["id"] for b in assistant_content
if isinstance(b, dict) and b.get("type") == "server_tool_use"
]
```
- `assistant_content`์ `server_tool_use`๊ฐ ์์ผ๋ฉด **๋ฌด์กฐ๊ฑด** dummy `web_search_tool_result`๋ฅผ ์ฝ์
ํจ
- ํ์ง๋ง ๊ฐ์ assistant message content ์์ ์ด๋ฏธ ํด๋น ID์ `web_search_tool_result`๊ฐ ์๋ ๊ฒฝ์ฐ, ์ด๋ฅผ ์ฒดํฌํ์ง ์์ ๋ถํ์ํ dummy๊ฐ ์์ฑ๋จ
- ์ด๋ก ์ธํด working_msgs์ ๊ตฌ์กฐ์ ๋ชจ์์ด ์์ฌ ๋ค์ API ํธ์ถ ์ 400 ๋ฐ์
3. **`_ensure_tool_results` ํจ์์ ๋งน์ (1559~1647๋ผ์ธ)**
- `assistant_content` ๋ด๋ถ์์ `server_tool_use`์ `web_search_tool_result`๊ฐ ํจ๊ป ์๋ ์ผ์ด์ค๋ฅผ ์ฒ๋ฆฌํ์ง ๋ชปํจ
- resolved_server ์ฒดํฌ๋ฅผ ๋ค์ user message์์๋ง ํ๋๋ฐ, ์ค์ result๊ฐ ๊ฐ์ assistant message ์์ ์๋ ๊ฒฝ์ฐ๋ฅผ ๋์นจ
## ์์ ๋ฐฉ๋ฒ
### Fix 1: `pending_server_ids` ๋ก์ง ์์ (1750๋ผ์ธ ๋ถ๊ทผ)
`server_tool_use` ID ์ค์์ **๊ฐ์ assistant_content ์์ ์ด๋ฏธ `web_search_tool_result`๋ก ํด๊ฒฐ๋ ๊ฒ**์ ์ ์ธํ๊ณ , ์ง์ง pending๋ง ์ฒ๋ฆฌ:
```python
# assistant_content ์์์ ์ด๋ฏธ ๊ฒฐ๊ณผ๊ฐ ์๋ server tool IDs
resolved_in_content = {
b.get("tool_use_id") for b in assistant_content
if isinstance(b, dict) and b.get("type") == "web_search_tool_result"
}
pending_server_ids = [
b["id"] for b in assistant_content
if isinstance(b, dict) and b.get("type") == "server_tool_use"
and b["id"] not in resolved_in_content
]
```
### Fix 2: `_ensure_tool_results` ์์ (1559๋ผ์ธ ๋ถ๊ทผ)
`_ensure_tool_results`์์ server_ids ํด๊ฒฐ ์ฌ๋ถ ์ฒดํฌ ์, **๊ฐ์ assistant message content ์์ `web_search_tool_result`**๋ resolved๋ก ์ธ์ :
```python
# server_tool_use IDs resolved IN the same assistant message content
resolved_in_same_msg = {
b.get("tool_use_id") for b in content
if isinstance(b, dict) and b.get("type") == "web_search_tool_result"
}
server_ids = [
b["id"] for b in content
if isinstance(b, dict) and b.get("type") == "server_tool_use"
and b["id"] not in resolved_in_same_msg # ์ด๋ฏธ ํด๊ฒฐ๋ ๊ฑด ์ ์ธ
]
```
## ๊ตฌํ ์ง์นจ
1. `read_file("telegram_bot.py", 1559, 1647)` ๋ก `_ensure_tool_results` ์ฌํ์ธ
2. `read_file("telegram_bot.py", 1697, 1780)` ๋ก tool processing ๋ฃจํ ์ฌํ์ธ
3. ์ ๋ ๊ณณ์ ๋ชจ๋ ์์ ํ ์์ฑ ๋ฒ์ ์ `write_file`๋ก ์ ์ฉ
4. ์์ ์ **ํด๋น ํจ์๋ง** ํ๊ฒํ
โ ์ ์ฒด ํ์ผ ์ฌ์์ฑ ๊ธ์ง
5. ์์ ํ `/errors` ๋ก๊ทธ์ ํจํด์ ํ์ธํด์ ๋์ผ ์๋ฌ๊ฐ ์ฌ๋ฐํ์ง ์๋์ง ๊ฒ์ฆ ๋ฐฉ๋ฒ์ ๋ฉ๋ชจ์ ๋จ๊ธธ ๊ฒ
## ๊ธฐ๋ ๊ฒฐ๊ณผ
์ค๋ ์ ์ ์์ค์ปฌ๋ ์ด์
๋ฑ web_search๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ ๋ฆฌ์์น ํ์คํฌ์์ 400 ์๋ฌ ์์ด ์์ฃผํ ์ ์์ด์ผ ํ๋ค.