85 lines
3.6 KiB
Markdown
85 lines
3.6 KiB
Markdown
# ChatGPT2API-Pro Function Calling Fix — 2026-07-04
|
||
|
||
## Context
|
||
|
||
Function calling через prompt engineering работал только для MCP (tools/call), но не для основного эндпоинта `/v1/chat/completions`. Модель не видела инструменты.
|
||
|
||
## Root Cause
|
||
|
||
В `api/chat2api.py` → `send_conversation()`:
|
||
- `inject_tools_into_messages()` импортировался, но **не вызывался** в основном пайплайне
|
||
- `parse_tool_calls_from_response()` и `build_openai_tool_call_response()` импортировались, но **не вызывались** при обработке ответа
|
||
|
||
## Fix Applied
|
||
|
||
### 1. Inject tools before sending
|
||
|
||
В `send_conversation()`, после парсинга `request_data`, перед вызовом `process()`:
|
||
|
||
```python
|
||
tools = request_data.get("tools")
|
||
if tools:
|
||
request_data["messages"] = inject_tools_into_messages(
|
||
request_data.get("messages", []), tools
|
||
)
|
||
```
|
||
|
||
### 2. Parse tool calls from response
|
||
|
||
В блоке `else` (не-стрим ответ), после получения `res`:
|
||
|
||
```python
|
||
if tools and isinstance(res, dict):
|
||
content = res.get("choices", [{}])[0].get("message", {}).get("content", "")
|
||
tool_calls, text_content = parse_tool_calls_from_response(content)
|
||
if tool_calls:
|
||
conv_id = res.get("conversation_id", "")
|
||
res = build_openai_tool_call_response(
|
||
model=res.get("model", request_data.get("model", "gpt-4o")),
|
||
tool_calls=tool_calls,
|
||
text_content=text_content
|
||
)
|
||
if conv_id:
|
||
res["conversation_id"] = conv_id
|
||
```
|
||
|
||
## Verified Results
|
||
|
||
```bash
|
||
# Tool call returns proper OpenAI format
|
||
curl ... -d '{"model":"gpt-4o","tools":[...],"messages":[...]}'
|
||
# → {"id":"...","choices":[{"message":{"tool_calls":[{...}],"role":"assistant"},"finish_reason":"tool_calls"}],"conversation_id":"..."}
|
||
```
|
||
|
||
- ✅ `finish_reason: "tool_calls"` (not "stop")
|
||
- ✅ `tool_calls[{id, type, function{name, arguments}}]` in message
|
||
- ✅ `conversation_id` preserved in response
|
||
- ✅ MCP tools/call returns `"HELLO"` from GPT-4o
|
||
|
||
## Round-Trip Limitation
|
||
|
||
Step 2 (sending tool result back) fails with HTTP 422:
|
||
```
|
||
"Invalid conversation body"
|
||
```
|
||
|
||
ChatGPT Web API doesn't accept `role: "tool"` messages in OpenAI format. Workaround: inject tool result as new user message.
|
||
|
||
## POW_DIFFICULTY Trap
|
||
|
||
**DO NOT USE `POW_DIFFICULTY=999999`** — это hex-строка, лексикографическое сравнение. `'07a120' <= '999999'` → True → PoW блокируется.
|
||
|
||
**USE `POW_DIFFICULTY=000001`** — минимальный hex, блокирует только если ChatGPT пришлёт `'000000'` (почти никогда).
|
||
|
||
## Token Expiry
|
||
|
||
Токен K12 (`[REDACTED-NAME]@gmail.com`) протух 2026-07-04 с `token_revoked`. Получен свежий из `https://chatgpt.com/api/auth/session` (JSON скинут пользователем). Структура: `accessToken` (JWT), `sessionToken` (encrypted), `user.id`, `account.planType: "k12"`, `account.structure: "workspace"`, `expires: "2026-10-02T14:22:11.016Z"`.
|
||
|
||
## Port Conflict on Windows
|
||
|
||
После `taskkill` порт 5005 остаётся в TIME_WAIT. Старые процессы (PID 36876, 8716, 22572, 63472, 67536) держали порт и запускали старый код без патчей. Нужно:
|
||
1. `netstat -ano | grep ":5005 " | grep LISTENING` → получить PID
|
||
2. `taskkill //F //PID <pid>`
|
||
3. `sleep 5` (ждать TIME_WAIT)
|
||
4. Проверить что порт свободен
|
||
5. Запустить сервер |