Skip to content

Commit 667ff58

Browse files
easonLiangWorldedtecheasonliang28easonLiangWorldedtechedelauna
authored
fix: resolve shared AI-SDK tool-call dedup and error handling gaps (Zoo-Code-Org#797) (Zoo-Code-Org#799)
* fix(tool-call-dedup): use compound key (id, name) for tool call deduplication * test(src): add missing tests for streaming tool call coverage - Add NativeToolCallParser tests for hasActiveStreamingToolCalls() and getStreamingToolName() - Add openai-compatible tests for streamText multiple parts and usage handling - Add Task integration tests for tool_call_start/delta/end lifecycle * test(task): stabilize streaming tool call compound-key specs * fix(api): correct openai-compatible import path to error-handler * fix(scripts): adapt parser-scoping model check to compound streaming keys The shared-SDK error-handle PR keys streaming tool-call state by the compound id::name key and emits the tool name on tool_call_end. Update the scoping model-check harness to construct the same compound keys and expect the name field, preserving all 924 schedules, invariants, and landmarks. * test: remove explicit any usages from streaming tool call and openai-compatible specs * fix(task): address streaming tool-call review findings - Route tool_call_delta through the compound (id, name) key so same-ID calls cannot share an accumulator; keep the deprecated ID-only lookup as a fallback for nameless legacy deltas - Lock the tracked raw-chunk name after start so a later chunk with a different name cannot orphan the parser entry or the dedup tracking - Resolve the tracked entry by id when a finalization end event misses the compound key, so cleanup always targets the real parser and dedup entries - Throw handleOpenAIError for streamed error parts so provider errors keep their status and provider name - Cover the paths with real Task-level integration tests, provider error-part and synchronous streamText failure tests, and per-call nativeArgs assertions * test: expect tool name in streamed tool_call_delta events The delta contract change adds the tracked tool name to every tool_call_delta parser event; update the parser-scope model check and the openai-compatible provider native-tools specs (lmstudio, openrouter, qwen) to assert the name alongside id and delta. * test(test-utils): expect tool name in streamed tool_call_delta events * fix(api): wrap streamed provider errors exactly once in openai-compatible * test(api): cover getLanguageModel() failure in completePrompt * fix(core): use injective streaming key for native tool calls makeStreamingKey escapes the :: delimiter inside each segment so distinct (id, name) pairs can no longer share a parser/Task key. Task now builds every dedup key through the helper and reuses the streaming key for the delta lookup. Adds regression tests: colliding pairs stay separate, malformed arguments finalize gracefully through real Task streaming, and result.usage failures are wrapped with status and provider name. * fix: harden streaming tool-call keys against backslash collisions and reconcile same-ID calls - makeStreamingKey now escapes the escape character and every colon, so segment pairs that differ only by a trailing backslash (e.g. (a\\, b::c) vs (a::b\\, c)) produce distinct keys * fix(task): reject any reused call ID regardless of entry displayed name The same-ID collision check compared entry.name against the incoming start name. Alias resolution renames a streaming entry to its canonical tool as its arguments stream in (the streamed name is kept in originalName), so a later start reusing the ID under the canonical name - e.g. search_and_replace followed by edit - matched the renamed entry's displayed name and was retained. The API history builder dedupes tool_use blocks by ID and results are matched by tool_use_id, so the second entry would be orphaned. The compound-key check already rejects an identical (id, name) start, so reject any remaining start whose ID is already present in assistantMessageContent, and add the alias-then-canonical regression case. * fix(api): preserve APICallError statusCode as .status; drop superseded local-copy test blocks (Zoo-Code-Org#797) - handleProviderError now falls back to the AI SDK APICallError statusCode so a real 429 reaches Task.backoffAndAnnounce's .status === 429 check (addressed maintainer review) - openai-compatible.spec.ts 429 test uses a real APICallError instead of a fabricated {status: 429} - removed two local-map-copy test blocks superseded by the real-Task integration tests (duplicate-tool-use-ids compound-key block; Task.streaming-tool-calls local-copy block) - PR description: corrected the Novita/Moonshot/Poe inheritance claim and documented the first-call-wins dedup rule * test: add mutation-gate kill tests and document unreachable defensive paths Closes the local preflight gaps found on the streaming-tool-call and error handler work, with every survivor either killed by a new test or documented: - error-handler: non-Error statusCode fallback, missing-status negative case, non-numeric statusCode rejection, and the null-throw path. - openai-compatible: configured temperature must reach generateText unchanged (guards the ?? 0 fallback). - Task: a real-Task stream test proving distinct call IDs are all accepted (the ID guard is per-ID, not a global lock) and a reused ID already held by an mcp_tool_use entry is rejected. - NativeToolCallParser: exact compound-key escape encoding, the naive-join collision case, and the defensive lookups' total behavior. - Task: six `Stryker disable next-line` directives with concrete proofs for the unreachable defensive checks around the compound streaming key. * fix(api): select a numeric status before falling back to statusCode A null or nonnumeric Error.status was taken as-is, so a nonnumeric value reached Task.backoffAndAnnounce's retry header and a numeric statusCode was ignored. The describe block named for Task integration only exercised the parser directly, so rename it to name the parser as its subject. * Assert the string statusCode is not copied into status The test only checked the instance type, which also passes if the string "429" is copied into .status - the exact regression the typeof guard prevents. * test(parser): cover the name lock with a post-start name change The Task test for the name change still passes without the lock because the id fallback finalizes the entry. Add a parser test that streams a different name after the start event: the delta and end events must still report the name the call started with. Verified by removing the lock - this test fails. * refactor(task): reuse the compound key already computed for the tool call The finalize call recomputed makeStreamingKey with the same arguments as the dedupKey two lines above it, and the defensive branch repeated the same pattern. Finalization and index cleanup now use one key, so they cannot diverge. tsc clean, 168 tests pass, ESLint --max-warnings=0 clean, no suppression change. * chore: fix Prettier drift and check formatting in CI --------- Co-authored-by: Eason Liang <[email protected]> Co-authored-by: easonLiangWorldedtech <[email protected]> Co-authored-by: Elliott de Launay <[email protected]> Co-authored-by: edelauna <[email protected]>
1 parent d1f5845 commit 667ff58

48 files changed

Lines changed: 12172 additions & 9713 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/ISSUE_TEMPLATE/add-provider-model.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
---
22
name: Add provider model
33
about: Add a provider model that isn't htere
4-
title: ''
4+
title: ""
55
labels: model
6-
assignees: ''
6+
assignees: ""
77
type: Task
8-
98
---
109

1110
Name of model: (GPT 5.4)

‎.github/ISSUE_TEMPLATE/bug_report.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ name: Bug report
33
about: Create a report to help us improve
44
title: "[BUG] "
55
labels: bug
6-
assignees: ''
6+
assignees: ""
77
type: Bug
8-
98
---
109

1110
**Describe the bug**
1211
A clear and concise description of what the bug is.
1312

1413
**To Reproduce**
1514
Steps to reproduce the behavior:
15+
1616
1. Go to '...'
1717
2. Click on '....'
1818
3. Scroll down to '....'

‎.github/ISSUE_TEMPLATE/bug_report.yml‎

Lines changed: 103 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,117 +3,117 @@ description: Report a broken behavior in plain language with a minimal reproduct
33
labels: ["bug"]
44
title: "[BUG] "
55
body:
6-
- type: markdown
7-
attributes:
8-
value: |
9-
Thank you for your report! Please search existing issues first:
10-
https://github.com/Zoo-Code-Org/Zoo-Code/issues
6+
- type: markdown
7+
attributes:
8+
value: |
9+
Thank you for your report! Please search existing issues first:
10+
https://github.com/Zoo-Code-Org/Zoo-Code/issues
1111
12-
- type: textarea
13-
id: problem
14-
attributes:
15-
label: Problem (one or two sentences)
16-
description: Describe what went wrong in plain language.
17-
placeholder: 'Example: "Expected the task to start, but nothing happened and no message appeared."'
18-
validations:
19-
required: true
12+
- type: textarea
13+
id: problem
14+
attributes:
15+
label: Problem (one or two sentences)
16+
description: Describe what went wrong in plain language.
17+
placeholder: 'Example: "Expected the task to start, but nothing happened and no message appeared."'
18+
validations:
19+
required: true
2020

21-
- type: textarea
22-
id: context
23-
attributes:
24-
label: Context (who is affected and when)
25-
description: Who sees this and in what situation? Keep it non-technical.
26-
placeholder: 'Example: "Happens to new users when starting a run from the New Run page with dark theme enabled."'
27-
validations:
28-
required: true
21+
- type: textarea
22+
id: context
23+
attributes:
24+
label: Context (who is affected and when)
25+
description: Who sees this and in what situation? Keep it non-technical.
26+
placeholder: 'Example: "Happens to new users when starting a run from the New Run page with dark theme enabled."'
27+
validations:
28+
required: true
2929

30-
- type: textarea
31-
id: steps
32-
attributes:
33-
label: Reproduction steps
34-
description: Provide clear, numbered steps so we can reproduce.
35-
placeholder: |
36-
1) Environment/setup (OS, extension version, relevant settings)
37-
2) Exact actions (clicks, inputs, commands)
38-
3) What you observed after each step
39-
validations:
40-
required: true
30+
- type: textarea
31+
id: steps
32+
attributes:
33+
label: Reproduction steps
34+
description: Provide clear, numbered steps so we can reproduce.
35+
placeholder: |
36+
1) Environment/setup (OS, extension version, relevant settings)
37+
2) Exact actions (clicks, inputs, commands)
38+
3) What you observed after each step
39+
validations:
40+
required: true
4141

42-
- type: input
43-
id: expected
44-
attributes:
45-
label: Expected result
46-
placeholder: e.g., "The task starts and shows progress."
47-
validations:
48-
required: true
42+
- type: input
43+
id: expected
44+
attributes:
45+
label: Expected result
46+
placeholder: e.g., "The task starts and shows progress."
47+
validations:
48+
required: true
4949

50-
- type: input
51-
id: actual
52-
attributes:
53-
label: Actual result
54-
placeholder: e.g., "The button appears disabled and no progress is shown."
55-
validations:
56-
required: true
50+
- type: input
51+
id: actual
52+
attributes:
53+
label: Actual result
54+
placeholder: e.g., "The button appears disabled and no progress is shown."
55+
validations:
56+
required: true
5757

58-
- type: textarea
59-
id: variations
60-
attributes:
61-
label: Variations tried (optional)
62-
description: Different browsers, devices, providers, or settings you tried.
63-
placeholder: e.g., "Tried Chrome/Firefox, disabling dark theme, switching providers."
58+
- type: textarea
59+
id: variations
60+
attributes:
61+
label: Variations tried (optional)
62+
description: Different browsers, devices, providers, or settings you tried.
63+
placeholder: e.g., "Tried Chrome/Firefox, disabling dark theme, switching providers."
6464

65-
- type: input
66-
id: version
67-
attributes:
68-
label: App Version
69-
description: What version of Zoo Code are you using? (e.g., v3.3.1)
70-
validations:
71-
required: true
65+
- type: input
66+
id: version
67+
attributes:
68+
label: App Version
69+
description: What version of Zoo Code are you using? (e.g., v3.3.1)
70+
validations:
71+
required: true
7272

73-
- type: dropdown
74-
id: provider
75-
attributes:
76-
label: API Provider (optional)
77-
options:
78-
- Anthropic
79-
- Amazon Bedrock
80-
- Chutes AI
81-
- DeepSeek
82-
- Featherless AI
83-
- Fireworks AI
84-
- Google Gemini
85-
- Google Vertex AI
86-
- Groq
87-
- LiteLLM
88-
- LM Studio
89-
- Mistral AI
90-
- Ollama
91-
- OpenAI
92-
- OpenAI Compatible
93-
- OpenRouter
94-
- Requesty
95-
- SambaNova
96-
- Unbound
97-
- VS Code Language Model API
98-
- xAI (Grok)
99-
- Not Applicable / Other
73+
- type: dropdown
74+
id: provider
75+
attributes:
76+
label: API Provider (optional)
77+
options:
78+
- Anthropic
79+
- Amazon Bedrock
80+
- Chutes AI
81+
- DeepSeek
82+
- Featherless AI
83+
- Fireworks AI
84+
- Google Gemini
85+
- Google Vertex AI
86+
- Groq
87+
- LiteLLM
88+
- LM Studio
89+
- Mistral AI
90+
- Ollama
91+
- OpenAI
92+
- OpenAI Compatible
93+
- OpenRouter
94+
- Requesty
95+
- SambaNova
96+
- Unbound
97+
- VS Code Language Model API
98+
- xAI (Grok)
99+
- Not Applicable / Other
100100

101-
- type: input
102-
id: model
103-
attributes:
104-
label: Model Used (optional)
105-
description: Exact model name (e.g., Claude 3.7 Sonnet). Use N/A if irrelevant.
101+
- type: input
102+
id: model
103+
attributes:
104+
label: Model Used (optional)
105+
description: Exact model name (e.g., Claude 3.7 Sonnet). Use N/A if irrelevant.
106106

107-
- type: textarea
108-
id: roo-code-tasks
109-
attributes:
110-
label: Zoo Code Task Links (optional)
111-
description: If you have any publicly shared Zoo Code task links that demonstrate the issue, paste them here.
112-
placeholder: Paste your Zoo Code share links here, one per line
107+
- type: textarea
108+
id: roo-code-tasks
109+
attributes:
110+
label: Zoo Code Task Links (optional)
111+
description: If you have any publicly shared Zoo Code task links that demonstrate the issue, paste them here.
112+
placeholder: Paste your Zoo Code share links here, one per line
113113

114-
- type: textarea
115-
id: logs
116-
attributes:
117-
label: Relevant logs or errors (optional)
118-
description: Paste relevant output or errors. Use triple backticks (```) for formatting.
119-
render: shell
114+
- type: textarea
115+
id: logs
116+
attributes:
117+
label: Relevant logs or errors (optional)
118+
description: Paste relevant output or errors. Use triple backticks (```) for formatting.
119+
render: shell

‎.github/ISSUE_TEMPLATE/feature_request.md‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
---
22
name: Feature request
33
about: What do you want to add to zoo?
4-
title: ''
4+
title: ""
55
labels: enhancement
6-
assignees: ''
6+
assignees: ""
77
type: Feature
8-
98
---
109

1110
**Is your feature request related to a problem? Please describe.**

0 commit comments

Comments
 (0)