[2026-09-28] eagle: personal/plans/runtime-pr4-openai-toolcalls-fixes.md personal/tech/adk-hostedagent-openai-mcp.md personal/tech/flaresolverr-taiga.md

This commit is contained in:
Alexey Martemyanov committed 2026-09-28 15:08:32 +06:00
1 parent c5d21f962a
commit 87f719f46b
3 files changed
+319 -4

No files matched your search

@@ -173,8 +173,67 @@ if _, ok := toolset.(requestProcessor); ok {
---
## 8. Wire-контракт OpenAI tool_calls: 4 дефекта (2026-09-28)
`hostedagent/openai.go` — наш патч tool_calls поверх runtime v2. Ревью PR
**normahq/runtime#4** дало вердикт «Request changes» с 4 воспроизводимыми дефектами.
Все подтверждены фактами и закрыты коммитом **`0937011`**. Разбор —
[[personal/plans/runtime-pr4-openai-toolcalls-fixes]].
| # | Дефект | Суть |
|---|---|---|
| P1-a | `genai.Schema` маршалится напрямую | типы уходят как `"type":"OBJECT"`/`"STRING"` — **genai использует uppercase-константы**, а OpenAI требует JSON Schema lowercase → 400 |
| P1-b | `content,omitempty` у tool-message | пустой результат → `{"role":"tool","tool_call_id":"..."}` без `content`, а он **обязателен** по схеме OpenAI → 400 |
| P2-a | Ветка `toolResponses` | `Content{FunctionResponse + Text}` терял текст (ADK такой Content разрешает) |
| P2-b | Декод аргументов | не-объект (массив/скаляр) падал в мусорный `{"raw":...}` → исполнитель не получал аргументов |
📌 **Как проверить фактом, а не глазами** (использовать при любой правке схем):
```go
// пробник: как реально сериализуется genai.Schema
out, _ := json.Marshal(&genai.Schema{Type: genai.TypeObject, Properties: ...})
t.Logf("%s", out) // → "type":"OBJECT" ← вот дефект
```
📌 **`genai.Schema.Nullable` — это `*bool`**, не `bool`. Компилятор ловит:
`non-boolean condition in if statement`.
🔴 **Урок про маскирующие тесты.** Существовавший
`TestOpenAIToolsFromConfig_WithParameters` проверял
`params["type"] != "OBJECT" && params["type"] != "object"` — **принимал обе формы**, поэтому
uppercase-баг жил незамеченным через все гейты. **Терпимый ассерт = отсутствующая проверка.**
Ассерт должен пинить ровно одно ожидаемое значение.
📌 **Формы аргументов, которые надо переживать** (все встречаются у провайдеров):
`{"query":"x"}` · `"{\"query\":\"x\"}"` · `"\"{\\\"query\\\":\\\"x\\\"}\""` (двойное) ·
`"[...]"` (массив) · `"scalar"`. Корень бага: `json.Unmarshal` в `map[string]any` **падает**,
если после unwrap получился не объект.
📌 **`FunctionCall.Args` по контракту — объект.** Поэтому не-объектный результат оборачивается
под стабильный ключ, а не отбрасывается.
### ⛔ CI PR из форка: `action_required`, 0s
Все PR-раны для веток из форка (`isCrossRepository: true`) висят в статусе **`action_required`
с длительностью `0s`** — GitHub не запускает workflow до одобрения мейнтейнером.
Диагностика:
```bash
gh run list --repo normahq/runtime --limit 10 # → completed | action_required | 0s
gh pr view 4 --json isCrossRepository,headRepository
```
Контроль: PR из ветки **в самом upstream** проходит нормально (`1m5s`). Это **внешний блокер**,
не дефект кода. Обход — ветка в upstream при наличии write-доступа.
### ⚠️ Копия рантайма в balda — отдельная!
`~/Developer/balda/runtime-local` — **отдельная копия** (не симлинк), подключена через
`replace github.com/normahq/runtime/v2 => ./runtime-local`. Её HEAD — коммит **balda**, а не
runtime. Правки в `~/Developer/runtime-local` **не доезжают** до боевого стенда автоматически.
Проверять: `git -C ~/Developer/balda/runtime-local rev-parse --short HEAD`.
---
## Связанные заметки
- [[personal/plans/runtime-pr4-openai-toolcalls-fixes]] — PR #4: 4 дефекта, фиксы, тесты
- [[personal/plans/balda-mm-phase4-agents]] — §19 (корень, фикс, тесты, приёмка), §16.2 и §18 опровергнуты
- [[personal/projects/balda/balda-kraken-mm-deploy]] — Фаза 3, образ `balda-mm:0.1.13`
- [[personal/tech/balda-go-fx-pitfalls]] — fx-питфоллы Балды