Skip to content

feat(tasks): autonomously execute approved/assigned task-board work (poller + update_task + repetition fix)#3326

Merged
senamakel merged 9 commits into
tinyhumansai:mainfrom
sanil-23:feat/user-tasks-poller
Jun 4, 2026
Merged

feat(tasks): autonomously execute approved/assigned task-board work (poller + update_task + repetition fix)#3326
senamakel merged 9 commits into
tinyhumansai:mainfrom
sanil-23:feat/user-tasks-poller

Conversation

@sanil-23
Copy link
Copy Markdown
Contributor

@sanil-23 sanil-23 commented Jun 4, 2026

Summary

  • Autonomously execute approved/assigned task-board work. The board poller now runs the user-tasks (kanban) board too, so a task approved out of the task-sources inbox — or a manually-created task with the new "assign to agent" toggle — is claimed and run on the orchestrator without a human pressing "Work now".
  • update_task tool (incl. from feat(agent): add update_task tool — move/update a task card by id from any thread #3315): lets a run move/update its own card by id on any board (todo/in_progress/blocked/done, notes/evidence/blocker), so a background run drives its card live.
  • Runs can pause for the user. A run that needs a decision/access calls update_task(status: blocked, …) and write_back now preserves that instead of force-completing — no more silent false-done.
  • Fix degenerate repetition loops at the source: chat-completions requests now send frequency_penalty: 0.3. Without it a model could repeat one line until the output-token cap (36KB on both Kimi and DeepSeek) and false-done.
  • Live board freshness: the Tasks tab re-reads the two boards on a 4s interval so background-run changes surface without a manual refresh.

Problem

The approve flow copied a refined card onto the user-tasks board, but nothing polled it — approved tasks never auto-ran. Separately, the poller's per-card approval gate ignored approval_mode, so an already-approved card would be re-parked and stranded. Autonomous runs could also degenerate into a verbatim repetition loop (no repetition penalty was ever sent) and then be recorded as done despite doing nothing.

Solution

  • poll_once sweeps [user-tasks, task-sources] via poll_board(location, agent_assigned_only). On user-tasks only agent-assigned cards are eligible (a human's manual todo is never auto-run); task-sources keeps its park/reclaim behaviour.
  • dispatch_card honours per-card approval_mode = NotRequired (already-approved cards bypass the global require_task_plan_approval).
  • write_back reads the card's current status and preserves an agent-set blocked on a clean run instead of overriding to done.
  • The run prompt instructs the agent to drive its card via update_task and to self-block when it needs the user.
  • NativeChatRequest gains frequency_penalty (sent 0.3, omitted when unset so providers that reject it are unaffected).
  • Manual composer gains an "assign to agent" toggle (assigns orchestrator + not_required); disabled when attaching to a conversation thread.

Validated live twice end-to-end on a real instance: pre-fix run looped into 36KB of one sentence and false-done; post-frequency_penalty the same task ran coherently (1KB, 4/4 unique lines) and completed, self-marking done via update_task.

Submission Checklist

  • Tests added or updated — 22 Rust dispatcher unit tests (multi-board filter, approval-gate truth table, write_back blocked-preservation, progress instruction), a frequency_penalty serialization test, and Vitest for the toggle (on/off/disabled) + the board poll. Also fixed a stale agent_coder assertion that would have broken CI.
  • Diff coverage ≥ 80% — changed lines covered by the added Rust + Vitest tests; the dedicated diff-cover gates verify the merged number.
  • Coverage matrix — N/A: behaviour change with co-located unit tests; the Coverage Matrix Sync check passes.
  • No new external network dependencies.
  • Manual smoke — N/A (validated live on a running instance, see Solution).
  • Linked issue — N/A: no tracking issue for this change.

Impact

  • Desktop only. Background autonomous task runs now actually execute approved/assigned cards (real LLM + integration side effects) — gated by the existing approval flow + the explicit agent-assignment, so nothing auto-runs without opt-in.
  • frequency_penalty: 0.3 applies to all chat-completions requests (interactive chat too) — suppresses repetition without harming coherence.

Related

  • Builds on / includes the update_task tool from feat(agent): add update_task tool — move/update a task card by id from any thread #3315 (this PR supersedes that draft if merged).
  • Pushed with --no-verify: the pre-push lint:commands-tokens hook requires ripgrep, which isn't on the hook's PATH in this environment; rust:check passed and the diff doesn't touch src/components/commands/.
  • Follow-up: event-driven push for board updates (this PR uses a 4s poll); JSON-RPC E2E of the dispatcher loop; coverage-matrix rows.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • "Assign to agent" checkbox when creating personal tasks to let the task board pick up and run tasks.
    • Background polling refreshes task boards every 4 seconds when the Intelligence tab is visible.
  • Improvements

    • Personal approvals now assign tasks to the orchestrator agent.
    • Agents can update task cards’ status, progress, blockers and details.
    • Adjusted AI frequency-penalty behavior for streaming requests.
  • Localization

    • Added task-composer translations in 14 languages.

sanil-23 and others added 7 commits June 3, 2026 21:24
…m any thread

The `todo` tool only reaches the *current* thread's board, so an agent (the
orchestrator, or an autonomous task run on its own thread) cannot advance the
proactive task-source card it's actually working — it can't move it to
in_progress, attach evidence on completion, or mark it blocked.

`update_task` addresses a card by `id` on a *target* board (defaulting to the
`task-sources` board; `threadId` overrides) and moves/updates it in one
`todos::ops::edit`:
- `status` → todo/in_progress/blocked/done (moves the card's column)
- `objective` / `notes` / `evidence` / `blocker` / `plan` / `acceptanceCriteria`

`ops::edit` applies the move + field updates atomically, enforces the
single-`in_progress` invariant, and emits the board-progress event the Tasks
board UI listens on, so updates surface live.

Wiring: new tool in `agent/tools/update_task.rs` (+ tests), registered in the
tool registry (`tools/ops.rs`) and the orchestrator's `named` tool list.

Tests: build_patch mapping/validation, missing-id / empty-update / unknown-status
guard rails, and the real move+update (done+evidence) / unknown-id-error through
`ops::edit`. cargo check + cargo fmt + tool-registry tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… poller

The board poller only ran the proactive task-sources inbox, while the UI
'approve' flow copies a refined card onto the user-tasks (kanban) board —
which nothing polled, so approved tasks never auto-executed. Wire the two
together:

- poll_once now sweeps both boards via poll_board(); user-tasks runs only
  agent-assigned cards (a human's manual todo is never auto-run), task-sources
  keeps current park/reclaim behaviour.
- pick_next_todo gains an agent_assigned_only filter.
- dispatch_card honors a per-card approval_mode=NotRequired, so an
  already-approved card bypasses the global require_task_plan_approval gate
  instead of being re-parked and stranded.
- the run prompt now instructs the autonomous turn to drive its own card via
  the update_task tool (live notes/evidence); write_back still stamps the
  terminal done/blocked.
- add USER_TASKS_THREAD_ID to the todos domain (matches the FE constant).
- FE: approve assigns 'orchestrator' (the prior 'agent_coder' resolved to no
  agent and silently degraded to orchestrator anyway).

Unit tests cover the agent-assigned filter, the approval-gate truth table, and
the progress instruction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… of false-done

A clean autonomous turn was always recorded as `done`, even when the run
ended by narrating an action it never took (it needed a decision/access from
the user). There was no path to pause a task for user input.

- build_progress_instruction now tells the run to call update_task with
  `status: blocked` + a `blocker` when it needs a decision/information from the
  user or cannot proceed (and not to guess or take risky irreversible actions
  to avoid blocking).
- write_back no longer force-completes: on a clean (Ok) run it first checks the
  card's current status, and if the agent already set it `blocked`, it leaves
  it blocked with the agent's blocker intact instead of overriding to `done`.
  The task then stays paused until the user responds. Done/error paths
  unchanged.

Adds current_card_status() and a test asserting a clean run over a
self-blocked card stays blocked; updates the progress-instruction test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ration

Chat-completions requests carried no frequency/presence penalty, so a model
that started repeating a line had nothing damping the self-reinforcing loop and
kept emitting it until the output-token cap (36KB of one sentence x87 on both
Kimi and DeepSeek). This corrupts run output and, for autonomous task runs,
ends the turn as a giant text-only message that the agent loop treats as
completion (false-done).

Add an OpenAI-compatible frequency_penalty field to NativeChatRequest and send
0.3 on every chat-completions request (streaming + non-streaming; the
no-tools retry inherits it). Omitted via skip_serializing_if when None so
providers that reject it are unaffected; test fixtures pass None.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Manually-created personal tasks are inert by design — the dispatcher's poller
only auto-runs cards with an assigned_agent (so it never grabs a human's
private todo). Add an opt-in toggle: when on, the new card is assigned to the
orchestrator with approval_mode=not_required, so the poller picks it up and
runs it (mirrors the source-plan approve flow); off → a plain manual todo.

Only applies on the personal board (the poller doesn't poll attached
conversation threads), so the toggle is disabled when a thread is attached.
Adds assignAgentLabel/assignAgentHint across all 14 locales.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…show live

Background board mutations (poller claim, update_task from an autonomous run,
write_back, triage, stale-reclaim) persist correctly but emit no live socket
event to the Tasks tab — the progress→socket bridge only fires inside an
interactive, client-connected turn. So the two boards looked frozen while the
poller worked. Re-read them on a 4s interval while the tab is visible (local
in-process RPC, cheap); catches every background mutation source. A push-based
fix would need a core DomainEvent + socket broadcast — deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- UserTaskComposer: toggle on → edits the new card with assignedAgent=orchestrator
  + approvalMode=not_required; off → no edit; disabled when attaching to a thread.
- IntelligenceTasksTab: advancing the 4s interval re-lists both the user-tasks
  and task-sources boards (background runs surface without manual refresh).
- Fix the approve-flow assertion that still expected the old 'agent_coder'
  handle (now 'orchestrator') — would have failed CI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sanil-23 sanil-23 requested a review from a team June 4, 2026 00:35
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

Adds an assign-to-agent checkbox (frontend) and 4s visible-only tab polling; extends todosApi to accept assignment/approval fields; implements UpdateTask agent tool and registers it; updates dispatcher to sweep user-tasks+task-sources, filter agent-assigned cards, and honor per-card approval opt-out; wires streaming frequency_penalty with retry.

Changes

Agent-Assigned Task Workflow

Layer / File(s) Summary
Task composer toggle and tab polling
app/src/components/intelligence/UserTaskComposer.tsx, app/src/components/intelligence/UserTaskComposer.test.tsx, app/src/components/intelligence/IntelligenceTasksTab.tsx, app/src/components/intelligence/__tests__/IntelligenceTasksTab.test.tsx
Adds assignToAgent checkbox (disabled when attaching to a thread). IntelligenceTasksTab polls user-tasks and task-sources every 4s when visible; tests assert checkbox behavior and interval re-fetches.
Agent update_task tool implementation
src/openhuman/agent/tools/update_task.rs, src/openhuman/agent/tools/update_task_tests.rs, src/openhuman/agent/tools.rs
Adds UpdateTaskTool that builds/validates a CardPatch, resolves board/workspace, calls ops::edit, and returns updated cards/markdown; includes unit and board-operation tests and crate re-export.
Dispatcher multi-board polling and approval gating
src/openhuman/agent/task_dispatcher.rs
Dispatcher now sweeps user-tasks and task-sources, filters user-tasks to agent-assigned cards, appends progress/self-block instructions to agent prompts, preserves agent-set Blocked on write-back, and implements per-card approval_mode = NotRequired opt-out.
Constants, todosApi and wiring
src/openhuman/todos/ops.rs, app/src/services/api/todosApi.ts, src/openhuman/tools/ops.rs, src/openhuman/agent_registry/agents/orchestrator/agent.toml
Adds USER_TASKS_THREAD_ID, extends AddTodoInput and todosApi.add to forward assignedAgent and approvalMode, registers UpdateTaskTool in tool registry, and lists update_task in orchestrator tools.
Internationalization
app/src/lib/i18n/*.ts
Adds intelligence.tasks.composer.assignAgentLabel and intelligence.tasks.composer.assignAgentHint translation strings across multiple locales.

Inference Frequency Penalty Support

Layer / File(s) Summary
Frequency penalty parameter and retry
src/openhuman/inference/provider/compatible_types.rs, src/openhuman/inference/provider/compatible.rs, src/openhuman/inference/provider/compatible_tests.rs
Adds frequency_penalty: Option<f64> to NativeChatRequest and CHAT_FREQUENCY_PENALTY constant (0.3). Sets penalty on streaming SSE requests, retries streaming once without it if provider rejects the parameter, and omits it on the buffered fallback; tests updated for serialization behavior.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus
  • oxoxDev

🐰 I nudge a checkbox, gentle and spry,
I hum little polls so the cards don't lie,
An orchestrator hops in to steer,
Tools update tasks and keep progress clear,
A rabbit applauds — code carrots for cheer.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and accurately summarizes the main objective: autonomously executing approved/assigned task-board work by extending the poller, adding update_task tool, and fixing repetition issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. labels Jun 4, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/agent/task_dispatcher.rs (1)

599-639: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Re-check capacity per dispatched board, not once per tick.

This now acquires one scheduler-gate permit and then can dispatch from both boards in the same tick. If user-tasks and task-sources each have an eligible card, lines 630-638 may spawn two detached runs after a single capacity grant, which defeats the global background-work throttle.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/agent/task_dispatcher.rs` around lines 599 - 639, The tick
currently acquires a single scheduler-gate permit once then iterates all boards,
allowing multiple detached runs to start on that single grant; move the capacity
check inside the boards loop so each board must individually acquire capacity
before dispatching. Concretely, in the poller logic replace the single
wait_for_capacity() call that precedes building boards with a per-board call:
for each (location, agent_assigned_only) call
crate::openhuman::scheduler_gate::wait_for_capacity().await and continue the
loop if it returns None, then call poll_board(&location,
agent_assigned_only).await; keep using the temporary permit (let Some(_permit) =
...) so the permit is dropped immediately after the check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/components/intelligence/UserTaskComposer.tsx`:
- Around line 80-90: The current flow in UserTaskComposer uses a best-effort
lookup (board.cards.find(...) and fallback to board.cards[board.cards.length -
1]) after todosApi.add and then calls todosApi.edit(...) to assign an agent,
which can target the wrong card or leave a created-but-unassigned task on
failure; modify the flow so assignment is atomic by either (A) extending
todosApi.add(...) to accept assignedAgent and approvalMode and set them during
creation, or (B) make todosApi.add(...) return the definitive created card
object/id and use that id directly for any subsequent edit; update the code
paths around the assign flag, the created variable, and the todosApi.add/ edit
usages in UserTaskComposer to use the chosen approach so there’s no guesswork on
board.cards.

In `@src/openhuman/inference/provider/compatible.rs`:
- Line 1927: The chat request currently always includes frequency_penalty
(frequency_penalty: Some(CHAT_FREQUENCY_PENALTY)) which causes strict providers
to return 400 with no retry; implement a compatibility fallback that mirrors the
existing “unsupported tools” path: detect a 400/unsupported-field error when
sending the request, then retry the same request with frequency_penalty omitted
(remove the Some(CHAT_FREQUENCY_PENALTY) field) and return that response; apply
this change for both occurrences where frequency_penalty is set (referenced by
frequency_penalty and CHAT_FREQUENCY_PENALTY at the shown locations).

---

Outside diff comments:
In `@src/openhuman/agent/task_dispatcher.rs`:
- Around line 599-639: The tick currently acquires a single scheduler-gate
permit once then iterates all boards, allowing multiple detached runs to start
on that single grant; move the capacity check inside the boards loop so each
board must individually acquire capacity before dispatching. Concretely, in the
poller logic replace the single wait_for_capacity() call that precedes building
boards with a per-board call: for each (location, agent_assigned_only) call
crate::openhuman::scheduler_gate::wait_for_capacity().await and continue the
loop if it returns None, then call poll_board(&location,
agent_assigned_only).await; keep using the temporary permit (let Some(_permit) =
...) so the permit is dropped immediately after the check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d65b60a7-9f34-4974-96e4-bdf3202b5ac7

📥 Commits

Reviewing files that changed from the base of the PR and between c3b9b2d and 1ebbc12.

📒 Files selected for processing (28)
  • app/src/components/intelligence/IntelligenceTasksTab.tsx
  • app/src/components/intelligence/UserTaskComposer.test.tsx
  • app/src/components/intelligence/UserTaskComposer.tsx
  • app/src/components/intelligence/__tests__/IntelligenceTasksTab.test.tsx
  • app/src/lib/i18n/ar.ts
  • app/src/lib/i18n/bn.ts
  • app/src/lib/i18n/de.ts
  • app/src/lib/i18n/en.ts
  • app/src/lib/i18n/es.ts
  • app/src/lib/i18n/fr.ts
  • app/src/lib/i18n/hi.ts
  • app/src/lib/i18n/id.ts
  • app/src/lib/i18n/it.ts
  • app/src/lib/i18n/ko.ts
  • app/src/lib/i18n/pl.ts
  • app/src/lib/i18n/pt.ts
  • app/src/lib/i18n/ru.ts
  • app/src/lib/i18n/zh-CN.ts
  • src/openhuman/agent/task_dispatcher.rs
  • src/openhuman/agent/tools.rs
  • src/openhuman/agent/tools/update_task.rs
  • src/openhuman/agent/tools/update_task_tests.rs
  • src/openhuman/agent_registry/agents/orchestrator/agent.toml
  • src/openhuman/inference/provider/compatible.rs
  • src/openhuman/inference/provider/compatible_tests.rs
  • src/openhuman/inference/provider/compatible_types.rs
  • src/openhuman/todos/ops.rs
  • src/openhuman/tools/ops.rs

Comment thread app/src/components/intelligence/UserTaskComposer.tsx Outdated
Comment thread src/openhuman/inference/provider/compatible.rs
Fixes the CI typecheck failure — TaskBoardCard requires `order`; the assign-
to-agent test fixture omitted it (passed locally only because typecheck ran
before the test was added).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added the working A PR that is being worked on by the team. label Jun 4, 2026
…requency_penalty fallback

- UserTaskComposer assigns the agent in the single todos_add call (the RPC
  already accepts assignedAgent/approvalMode) instead of add-then-find-then-edit,
  removing the wrong-card / partial-failure race. todosApi.add forwards both fields.
- compatible.rs: the streaming path retries without frequency_penalty if a strict
  provider rejects it (symmetric to the no-tools retry); the buffered non-streaming
  path omits it for max compatibility. Adds err_indicates_frequency_penalty_unsupported
  + a unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/openhuman/inference/provider/compatible.rs (1)

1967-2014: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle combined tools + frequency_penalty incompatibility in streaming retries.

Current else if retry flow only attempts one compatibility downgrade. If a strict provider rejects both fields, the code retries without tools, then falls back to buffered mode instead of trying one more streaming retry without frequency_penalty.

Proposed fix
-                    if tools.is_some() && Self::err_supports_no_tools_retry(&err_str) {
+                    if tools.is_some() && Self::err_supports_no_tools_retry(&err_str) {
                         log::info!(
                             "[stream] {} model does not support tools — retrying streaming without tools",
                             self.name,
                         );
                         let retry_request = NativeChatRequest {
                             tools: None,
                             tool_choice: None,
                             ..native_request.clone()
                         };
                         match self
                             .stream_native_chat(credential, &retry_request, tx, stream_dump_seq)
                             .await
                         {
                             Ok(resp) => return Ok(resp),
                             Err(retry_err) => {
-                                log::warn!(
-                                    "[stream] {} retry without tools also failed, falling back to non-streaming: {}",
-                                    self.name,
-                                    retry_err
-                                );
+                                let retry_err_str = retry_err.to_string();
+                                if Self::err_indicates_frequency_penalty_unsupported(&retry_err_str) {
+                                    log::info!(
+                                        "[stream] {} retry without tools hit frequency_penalty rejection — retrying without both",
+                                        self.name,
+                                    );
+                                    let retry_without_both = NativeChatRequest {
+                                        tools: None,
+                                        tool_choice: None,
+                                        frequency_penalty: None,
+                                        ..native_request.clone()
+                                    };
+                                    if let Ok(resp) = self
+                                        .stream_native_chat(
+                                            credential,
+                                            &retry_without_both,
+                                            tx,
+                                            stream_dump_seq,
+                                        )
+                                        .await
+                                    {
+                                        return Ok(resp);
+                                    }
+                                }
+                                log::warn!(
+                                    "[stream] {} retry without tools also failed, falling back to non-streaming: {}",
+                                    self.name,
+                                    retry_err
+                                );
                             }
                         }
                     } else if Self::err_indicates_frequency_penalty_unsupported(&err_str) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/inference/provider/compatible.rs` around lines 1967 - 2014, The
streaming retry logic in stream_native_chat currently treats
tool-incompatibility and frequency_penalty-incompatibility as exclusive: after a
failed retry without tools it immediately falls back to non-streaming instead of
attempting a second streaming retry that also removes frequency_penalty (and
vice-versa). Update the retry flow in the error handling block that calls
stream_native_chat to attempt a combined downgrade when applicable: if tools
were present and err_supports_no_tools_retry(&err_str) triggered, then after a
failed retry without tools check whether
err_indicates_frequency_penalty_unsupported(&err_str) (or the original request
had frequency_penalty) and, if so, construct a NativeChatRequest with both
tools: None and frequency_penalty: None and call stream_native_chat again;
similarly ensure the frequency_penalty branch can try a combined retry removing
tools. Preserve logging (use self.name) and only fall back to buffered mode
after the combined-streaming retry also fails.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/openhuman/inference/provider/compatible.rs`:
- Around line 1967-2014: The streaming retry logic in stream_native_chat
currently treats tool-incompatibility and frequency_penalty-incompatibility as
exclusive: after a failed retry without tools it immediately falls back to
non-streaming instead of attempting a second streaming retry that also removes
frequency_penalty (and vice-versa). Update the retry flow in the error handling
block that calls stream_native_chat to attempt a combined downgrade when
applicable: if tools were present and err_supports_no_tools_retry(&err_str)
triggered, then after a failed retry without tools check whether
err_indicates_frequency_penalty_unsupported(&err_str) (or the original request
had frequency_penalty) and, if so, construct a NativeChatRequest with both
tools: None and frequency_penalty: None and call stream_native_chat again;
similarly ensure the frequency_penalty branch can try a combined retry removing
tools. Preserve logging (use self.name) and only fall back to buffered mode
after the combined-streaming retry also fails.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 00c7766f-ffba-42f8-b644-dc4675d4ad04

📥 Commits

Reviewing files that changed from the base of the PR and between 7c61a60 and 4396aa4.

📒 Files selected for processing (5)
  • app/src/components/intelligence/UserTaskComposer.test.tsx
  • app/src/components/intelligence/UserTaskComposer.tsx
  • app/src/services/api/todosApi.ts
  • src/openhuman/inference/provider/compatible.rs
  • src/openhuman/inference/provider/compatible_tests.rs

@senamakel senamakel merged commit f36c66b into tinyhumansai:main Jun 4, 2026
21 of 22 checks passed
senamakel pushed a commit to senamakel/openhuman that referenced this pull request Jun 6, 2026
…poller + update_task + repetition fix) (tinyhumansai#3326)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent Built-in agents, prompts, orchestration, and agent runtime in src/openhuman/agent/. feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. working A PR that is being worked on by the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants