| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
✨ Support full thread management (#3917) * 📃 Merge cursor rules into universal AGENTS.md and skills * ✨ Support full thread management * ✨ Support full thread management phase2 * 🧪 Add test files * 🧪 Add test files * 🧪 Add test files * ✨ Thread management fulfilling platform constraint * 🧪 Add test files * 🧪 Add thread interface * 🧪 Add test files | 8 天前 | |
[fix] 合并 CodeAgent 静默重试与显式终止协议 (#3973) * [codex] fix(agent): silently retry transient model errors (#3965) * fix(agent): retry transient model failures silently * fix(model): support OpenAI httpx2 timeout client * test(model): add deterministic OpenAI-compatible mock * fix(agent): keep stream runtime within line budget * fix(agent): enforce explicit CodeAgent termination * fix(test): restore CodeAgent CI compatibility * test(agent): keep develop isolation checks compatible * fix(agent): hide protocol repair generation stream * fix(ci): satisfy CodeAgent quality gate * test(model): cover retry classification branches | 1 天前 | |
fix: harden quota, tag, model and API-key contracts found by daily test suite (#4002) - quota: reject negative GB/MB inputs and warning>=critical threshold pairs (previously persisted as 200 with semantically inverted config) - api-key: mask access_key in /api-keys and /user/tokens list responses; only create/refresh may return the full secret once - tag: translate the DB trigger's 'Tag assignment limit exceeded' (without the 'Resource ' prefix) into a structured 409, and flush replacement deletes before inserts so a full tag replacement no longer trips the assignment-capacity trigger - model: propagate ValueError from create/batch_create so duplicate display names map to 409 and malformed batch entries to 422 instead of 500; include model_type in /model/llm_list items; tolerate quick-config entries without model_repo in get_model_name_from_config - test: align model service tests with the ValueError passthrough contract Co-authored-by: chase <byzhangxin11@126.com> | 5 小时前 | |
import ali and volc stt model (#2934) * improve codecov * implement asr * import ali qwen realtime tts model * import volc realtime tts model * improve code readability * improve code readability * add test files * improve code readability and maintainability by adding comments and improving variable names. * improve test files * improve test files * improve test files * add test code for ali voice model to improve coverage * add test code for ali voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * add test code for voice model to improve coverage * save stt only * add test files * add user guide for voice model and Modify the prompt in the base_utl input box of the voice model | 4 个月前 | |
merge(develop): merge v2.6.1 hotfixes from hotfix/v2.6.1 (#3998) * 🐛 Fix(evaluation): run trials in runtime service (#3954) * fix(evaluation): run trials in runtime service Route trial evaluations through the authenticated Config-to-Runtime proxy and use Config's manager only for creation-stage preparation. Keep Agent execution and evaluator scoring in Runtime. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub config thread manager Keep pure-logic service import tests aligned with the Config and Runtime thread-manager split. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub runtime jwt helper * test(evaluation): cover trial proxy error paths * Fix/override delete (#3958) * Fix: override dialog only shows override values, not model defaults (deleted params no longer reappear) * Fix: custom param deletion persists (null markers), per-agent capacity overrides take effect, and edit-dialog connectivity probe uses stored api_key * Fix: rename ModelRequest.model_id to probe_model_id - model_dump() is spread into INSERT column lists, so a model_id field injected an explicit NULL primary key and broke model creation * Fix: move probe_model_id to a dedicated ModelProbeRequest subclass - ModelRequest.model_dump() is spread into INSERT column lists, so any non-column field breaks model creation (Unconsumed column names) * Fix: editing/adding a model no longer steals the occupied default-model slot - persistCustomLocalConfig now only writes the slot when it is empty (onboarding) or the submitted model already occupies it * Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots) * Revert "Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots)" This reverts commit 2376aa50ccb0f170e5412a14c5aee33798ca1f06. * Fix: VLM connectivity probe never found the local test image - the gateway adapter's relative dirname chain resolved two levels short of the package root, so every probe silently fell back to a public URL that is unreachable in offline deployments. Anchor both probe copies on nexent.__file__ so the path survives module moves. --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * cherry-pick: HITL bugfixes from PR #3948 into hotfix/v2.6.1 (#3959) * Bubfix: guarantee event order, harden chunk buffer, SSE-subscribe controller, and break adapter on terminal human_run (#3948) * fix(hitl): preserve correct event ordering between observer chunks and human_interaction requests Root cause: the worker thread writes human_interaction events synchronously via SQLAlchemy in ask_user, while model_output_thinking/parse observer messages flow through the async consumer and are flushed only on a batched threshold (32 chunks or 250ms). When the worker suspends before that flush fires, human_interaction gains a lower event_seq number than the already-buffered observer chunks, causing the SSE replay stream to show them in the wrong order. Fix: replace the plain async-for consumer loop with a manual asyncio.wait iterator using a 50ms timeout. Once the worker finishes producing model output (i.e. right before ask_user), the loop times out and flushes any buffered observer chunks to the DB first, guaranteeing they precede the subsequent human_interaction row. Empty queue idle periods are essentially zero-cost; overall DB write frequency stays on par with the original. * fix(hitl): guarantee observer chunks precede human_interaction in DB event order When the agent invokes ask_user, two independent write paths caused the human_interaction row to be persisted BEFORE model_output_thinking / parse chunks, breaking the SSE replay ordering: the worker thread writes HITL events synchronously via SQLAlchemy, while observer messages flow through the async consumer which only flushes on a batched threshold. Fix: introduce a thread-safe shared chunk buffer on RuntimeInteractionPort (port.add_chunk / port.take_chunks). The async consumer pushes every processed chunk there; the worker thread calls flush_chunks_until_idle() before dispatching any HITL event — it polls the shared buffer and waits for the async loop to drain the observer queue (20ms idle window, 500ms max wait), then persists every chunk in its own transaction. This guarantees chunk event_seq < human_interaction event_seq regardless of async scheduling latency. Also fix ImportError: openai 2.50 removed the httpx2 module. OpenAIModel now falls back from httpx2 to httpx at import time. * test(hitl): cover shared chunk buffer, flush_chunks_until_idle, and httpx2-fallback paths Add unit tests for the RuntimeInteractionPort thread-safe chunk buffer and the flush_chunks_until_idle poll loop that guarantees observer chunks precede human_interaction events in DB order. All five HITL entry points (dispatch / boundary / receipt / finish / _wait_until_ready) are verified to invoke the idle flush before opening their transaction. Also add two tests for the openai_llm httpx2 → httpx ImportError fallback introduced to support openai >= 2.50 where the httpx2 shim was removed: one covers the fallback path, one confirms httpx2 still wins when present. * fix(hitl): address 4 review comments — hard deadline, emit_in_flight, peek_chunks, try/except safety Fix 4 real issues flagged by github-code-review: 1. Non-resettable hard_deadline in flush_chunks_until_idle — previously reset on every drain, meaning a model that kept producing chunks could stall the worker forever. deadline is now computed once at entry and the sleep call clips to hard_deadline - now. 2. _emit_in_flight Event bridges the async emit path and the worker's idle poll. Without this, buffer-empty = 'persisted' was confused with buffer-empty = 'taken for emit but still in run_blocking queue'. The worker now checks both 'buffer empty for settle_ms' AND 'no emit in flight' before deciding the async side is truly idle. 3. peek_chunks() replaces the take-put-back pattern in _flush_if_due. Previously the async loop drained the buffer, decided it was not yet due, then put everything back. That transiently-empty window (16 us normally, arbitrarily long under GIL/GC/preemption) was enough for the worker's 20 ms poll to mis-fire. We now peek (read count, no drain) and only take_chunks when we actually intend to persist. 4. emit_chunks wrapped in try/except that puts drained chunks back into the shared buffer before re-raising, and finish() wraps its flush call in try/except: pass. Guarantees (a) no chunk loss on DB failure and (b) the terminal human_run row is always written even if the flush step fails. Tests added: - 12 pure-mock unit tests in test_runtime_port_chunk_buffer.py cover hard_deadline, _emit_in_flight, peek_chunks, begin_emit/end_emit, try/except path, and every HITL entry-point's flush-before-transaction. - 1 async execute_attempt integration test in new test_application_execute_attempt.py drives the full consumer loop through _flush_if_due (peek → take → begin/end_emit) and the final flush, verifying that every patch line added in application.py is hit. * perf(hitl): stop polling while SSE stream is active, fallback to 5s when disconnected When isRunning=true the EventSource already pushes human_interaction and human_execution events in real time — the 1.5s polling loop duplicated that work, hitting the DB and re-rendering the frontend for every tick. Disable polling entirely while the SSE stream is alive, and drop to 5s intervals only when the stream is closed (e.g. page load before the first run, or after a run finishes) so we can still discover WAITING_HUMAN requests that were created while the client was disconnected. Add isRunning to the useEffect dependency array so the polling cadence resets immediately when the SSE connection state changes. * perf(hitl): replace polling with SSE subscription and move snapshot off the write path Frontend — /conversation polling → /{run_id}/events SSE: - Replace the 5s conversation snapshot polling with a native EventSource subscription to the backend's /{run_id}/events SSE stream. Discovery is now one-shot: conversationId change and the agent stream pause (isRunning true→false), the exact moment a HITL run is most likely to exist. The SSE stream then keeps run state live with native auto-reconnect. - Add dual guards inside refresh() to absorb the thundering herd from adapter.onHumanInteractionEvent (fires once per HITL SSE chunk) plus our own SSE effect: (1) in-flight dedupe — one snapshot absorbs all concurrent callers and returns cached state; (2) 3s minimum interval so bursts after the in-flight resolves do not immediately re-hit DB. - Use a runRef mirror so refresh() stays stable and downstream effects do not re-run on every snapshot. - Detect terminal status inside SSE onmessage and proactively es.close() to prevent EventSource from reconnecting forever against COMPLETED runs. Backend — snapshot off the write path: - Add repository.read_only() context manager: plain SELECT without WITH FOR UPDATE, no transaction, no flush, no _expire scan. Pure reads must not contend with worker writes on the same row lock. - Add service.light_snapshot() using read_only. Retain snapshot() as a writer-path API for any future lock-held callers. - Route conversation_snapshot, run snapshot endpoint, and both snapshot calls inside stream_run() through light_snapshot. - Move expiration to the writer path: decide() still calls _expire inline before processing each request, and expire_waiting() remains the periodic scheduler sweep. Impact: conversation snapshot calls drop from 12+/min (polling) or 10+/s (burst from adapter + SSE) to at most one every 3s. Each call is now two plain SELECTs instead of a lock-held transaction with a possible write from _expire. Read and write paths are fully decoupled. * fix(hitl): detect terminal human_run in adapter and break stream so isRunning flips false After a HITL run reaches FAILED/COMPLETED, Assistant-UI's isRunning stayed true — the stop button remained visible and new messages went into the queue buffer instead of being sent normally. The root cause is that isRunning is driven entirely by the ChatModelRun generator lifetime, which only returns when the backend SSE HTTP connection closes (reader.read() -> done=true). The backend stream_run loop can hang on heartbeat even after the run is terminal when the SSE was opened during WAITING_HUMAN with attempt_active=true: the break condition requires both cursor >= event_seq AND (terminal status OR WAITING_HUMAN with attempt_active=false and empty rows). If continueHitl fires mid-flight with a stale after_event, the cursor never catches up, so the SSE stays alive forever and the generator never returns. Stop depending on the backend closing first. Inside the adapter's SSE chunk loop, detect a terminal human_run event (status in COMPLETED, FAILED, STOPPED, EXPIRED), set a hitlTerminal flag, break the inner for-loop, and let the outer while-loop exit via the same flag on the next iteration. Assistant-UI sees the generator return and flips isRunning false immediately. Only affects HITL streams — the normal non-HITL agent path never emits human_run events so this branch is never taken. * test(hitl): update mock from snapshot to light_snapshot after read-path refactor test_human_interaction_app.py still mocked service.snapshot after commit 288ae4e69 moved conversation_snapshot and the run snapshot endpoint to service.light_snapshot (read-only path, no lock, no _expire). The fixture return_value and the two assert_called_once_with/assert_not_called assertions all referenced the old method name, causing CI to fail because MagicMock.snapshot was never called. * test(hitl): raise diff coverage above the 90% merge gate Codecov reported 70.43% patch coverage (target 90%) because new error and race paths in the HITL changes had no tests. Add mocked unit tests for: leftover chunk flush in execute_attempt's finally block before the failed finish, CancelledError scope and stop-event fallbacks, RunTerminated finish race, recovery-required outcome, and chunk iterator aclose failure tolerance; runtime_port in-flight emit busy detection, chunk restoration when emit_chunks raises, and terminal status persistence on flush failure; light_snapshot/read_only service behavior with signed tenant and user scoping; and the httpx fallback when openai._base_client.httpx2 is absent. Measured locally with CI-equivalent per-file pytest isolation: patch coverage 202/202 = 100%. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * refactor(hitl-test): dedupe fake port setup to satisfy SonarCloud duplication gate SonarCloud failed the quality gate with new_duplicated_lines_density=5.2% (threshold 3%), caused solely by test_application_execute_attempt.py: the inline _Port stub in the flush test and the one in _run_execute_attempt duplicated ~69 lines (2 CPD blocks, 14.4% file density). Extract a shared _build_port_class/_make_port_factory plus a _patched_application context manager and _execute_attempt_args so both call sites reuse a single definition; drop dead code (last_flush, install/monkeypatches, unused imports) and fix the latent bare-contextmanager NameError by using contextlib.contextmanager. No behavioral change; all 8 tests pass. * fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm openai >= 2.50 removed the httpx2 shim from openai._base_client. The bare import httpx2 causes ImportError in CI and on systems with recent openai versions. This restores the try/except fallback introduced in PR #3948 commit 9521b934 and later accidentally reverted by commit d086da259. * Revert "fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm" This reverts commit 8e06ce348dfc8c34baf42c6bfa8211883d0a2c61. * 🐛 Bugfix: Fixed an issue where the sandbox container user lacked the permissions to create folders and files. (#3963) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngi… (#3962) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngineProvider - get_provider_models routed every provider through the OpenAI-compatible adapter, so ModelEngine batch import failed (wrong endpoint path /open/router/v1/models, self-signed cert, custom type taxonomy, missing per-model base_url). The dedicated class existed but was never wired in. * chore: ModelEngine catalog base_url placeholder - preset public URL is wrong for private deployments, placeholder communicates the required /open/router/v1 path format --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * [codex] fix(agent): silently retry transient model errors (#3965) * fix(agent): retry transient model failures silently * fix(model): support OpenAI httpx2 timeout client * test(model): add deterministic OpenAI-compatible mock * fix(agent): keep stream runtime within line budget * Fix: AIDP knowledge base bug fix (#3967) * Fix: editing a ModelEngine model no longer flips ssl_verify to True - the update path only checked api_key emptiness while the create path also exempts open/router URLs (ModelEngine self-signed certs). The edit dialog prefills the real key and always submits it, so any edit silently broke connectivity. Exemption now checks the payload URL with a fallback to the stored record; batch-edit groups get the same protection * refactor: extract MODEL_ENGINE_URL_MARKER constant (SonarCloud S1192) and use a placeholder domain in test fixtures - no behavior change * [fix] enforce explicit CodeAgent termination and silent recovery (#3969) * fix(agent): enforce explicit CodeAgent termination * fix(test): restore CodeAgent CI compatibility * cherry-pick: HITL reliability fixes from PR #3977 into hotfix/v2.6.1 (#3981) * Fix StopAsyncIteration leak in execute_attempt finally block Root cause: when the agent chunk stream exhausted normally, the finally block awaited the already-consumed anext_task, re-raising StopAsyncIteration which was not suppressed by the existing CancelledError handling. The leftover chunk flush was skipped, successful runs were marked as failed, and the claiming scheduler job logged errors. Fix: reset anext_task to None before breaking out of the consumption loop so the finally block skips the await and always reaches the leftover flush and terminal finish() write. Tightened the regression test to assert that a normally exhausted stream does not leak StopAsyncIteration and that finish() is called. Also deduplicated the two _Port stub classes via a shared factory to satisfy the SonarCloud new_duplicated_lines_density gate. * Fix HITL form not appearing until page refresh Root cause: the frontend discovery chain rate-limited every refresh() with a 3s min interval and in-flight coalescing, silently dropping the critical human_run/human_interaction events that follow an ask_user suspension. The run event stream goes quiet afterwards, so nothing re-triggered the snapshot and the form only appeared after a manual page reload. Fix: refresh() now takes a force flag that bypasses the throttle; force callers arriving while a snapshot is in flight are re-run via a trailing refreshRef invocation instead of being dropped. SSE human_run/human_interaction/human_decision/human_execution messages and the chat-adapter onHumanInteractionEvent callback now force refresh. * Fix SSE chunk/HITL event ordering race under real server load Root cause: chunk persistence and HITL event writes ran in two threads (async consumer via run_blocking on the control-io lane, worker thread synchronously) with seq assigned at DB row-lock acquisition time. Two race windows reordered messages on loaded servers but never locally: (1) flush_chunks_until_idle's 500ms hard deadline fired while the async drain was still in flight, so the HITL row committed before chunks produced earlier (form appearing before model output); (2) worker emit_chunks and the async _flush_if_due drained concurrently without mutual exclusion, so seq order followed lock acquisition instead of production order. Fix: replace the begin_emit/end_emit Event with a shared threading.Lock and move take_chunks+emit_chunks into one atomic critical section (drain_and_emit) used by both the async consumer and the worker flush. The hard deadline may now only fire once the lock is free, guaranteeing in-flight drains commit before the caller writes its HITL transaction. Added regressions: flush waiting for an in-flight drain past its deadline, and concurrent drains preserving chunk production order. * fix(hitl): recover pending form when SSE delivery stalls silently Root cause: form discovery relied solely on a single EventSource plus refresh() with no fallback. A half-open connection (e.g. hung dev proxy) never raises an error event or reconnects, so human_interaction events are lost until a manual page refresh. A hung snapshot fetch could also keep refreshInFlight stuck forever, silently dropping every later refresh, including forced ones. Changes: - Poll the read-only snapshot every 5s while a run is active; the tick shares the refresh throttle and in-flight guard, so it adds no load while SSE delivery is healthy and discovers a pending form within 5s when the stream stalls - Add a 15s AbortSignal timeout to human-interaction client requests so a hung fetch releases the in-flight guard instead of bricking it - Wrap the human_run chunk JSON.parse in the chat adapter with try/catch: the stream loop has a finally but no catch, so a malformed payload would silently kill the whole chat stream read loop * fix(hitl): stop parked human-input waits from consuming scheduler slots Root cause: while a run waits for a human decision, its executor task parks inside _wait_until_ready and the lease renewal loop keeps the lease alive, so the run occupies one HITL_MAX_CONCURRENCY slot for up to HITL_WAIT_SECONDS (default 24h). With HITL enabled every non-debug chat is dispatched through this scheduler, so two unattended forms filled the default concurrency of 2 and froze all conversations: new agent/run streams only emitted heartbeats because READY runs were never claimed. Fix: add LeaseScheduler.mark_waiting so executors can flag themselves as parked on external input. Slot capacity is now max_concurrency minus executing jobs only (running minus waiting), and the waiting flag is cleared in the job's finally block. RuntimeInteractionPort relays enter/exit of _wait_until_ready through a wait_reporter callback, covering resume, termination and lease loss paths. The reporter degrades safely: a stale SDK copy without mark_waiting falls back to slot-consuming waits, and call_soon_threadsafe is wrapped in a lambda because it does not forward keyword arguments. Config: raise the env example defaults from 24h to 1h waits and concurrency 2 to 100, since waiting runs no longer consume execution slots. Tests: new test_waiting_jobs_do_not_consume_concurrency; scheduler suite 14/14, HITL service 12 passed, runtime and app suites 43/43. * test(scheduler): fix flaky waiting-concurrency assertion on fast event loops Root cause: job 2's executor completed instantly after appending to started, so its done-callback could discard it from _running before the active_count == 2 assertion ran. On Linux CI the event loop schedules that callback first, making the test fail intermittently. Fix: both executors now park on the shared gate via separate running events, so the assertion observes a stable running set instead of a transient window. * Fix: drop unrelated AIDP interface refactor from the AIDP knowledge base fix (#3980) The AIDP knowledge base fix reached hotfix/v2.6.1 through PR #3967, which also carried two unrelated upstream changes that this release branch never had: - #3909 Knowledge base interface optimization (AIDP UI refactor) - #3930 support AIDP knowledge file deletion and download Both are removed here so the release line keeps only the bug fix. - Restore the AIDP frontend components to their pre-refactor layout and drop the helper modules only the refactor used: AidpKnowledgeBaseModalParts, useAidpGroupOptions, aidpUploadUtils. - Drop the #3930 document remove/download endpoints from services/api.ts and the AIDP translations that only those screens referenced. - Keep the fix itself unchanged: knowledge-base scoped Channels and KnowledgeFiles/History paths, all-status document listing, keyword search, status labels (UPLOADING / PROCESSING / EXTRACTING) and the upload-triggered polling. Verified: - pytest test/ext_components/aidp -q -> 583 passed - frontend `npm run type-check` (tsc --noEmit) -> no errors * fix(agent): accept reasoning-prefixed code actions (#3990) * refactor: remove human interaction features and related configurations (#3988) * refactor: remove human interaction features and related configurations * refactor: remove human interaction features and related configurations --------- Co-authored-by: cj2026-bit <647646783@qq.com> Co-authored-by: lijiayang619 <1170349871@qq.com> Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> Co-authored-by: bernard1234 <840646206@qq.com> Co-authored-by: panyehong <91180085+YehongPan@users.noreply.github.com> Co-authored-by: Jason Wang <56037774+JasonW404@users.noreply.github.com> Co-authored-by: gs-aion <gs597153711@qq.com> Co-authored-by: Dallas98 <40557804+Dallas98@users.noreply.github.com> Co-authored-by: chase <byzhangxin11@126.com> | 8 小时前 | |
merge(develop): merge v2.6.1 hotfixes from hotfix/v2.6.1 (#3998) * 🐛 Fix(evaluation): run trials in runtime service (#3954) * fix(evaluation): run trials in runtime service Route trial evaluations through the authenticated Config-to-Runtime proxy and use Config's manager only for creation-stage preparation. Keep Agent execution and evaluator scoring in Runtime. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub config thread manager Keep pure-logic service import tests aligned with the Config and Runtime thread-manager split. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub runtime jwt helper * test(evaluation): cover trial proxy error paths * Fix/override delete (#3958) * Fix: override dialog only shows override values, not model defaults (deleted params no longer reappear) * Fix: custom param deletion persists (null markers), per-agent capacity overrides take effect, and edit-dialog connectivity probe uses stored api_key * Fix: rename ModelRequest.model_id to probe_model_id - model_dump() is spread into INSERT column lists, so a model_id field injected an explicit NULL primary key and broke model creation * Fix: move probe_model_id to a dedicated ModelProbeRequest subclass - ModelRequest.model_dump() is spread into INSERT column lists, so any non-column field breaks model creation (Unconsumed column names) * Fix: editing/adding a model no longer steals the occupied default-model slot - persistCustomLocalConfig now only writes the slot when it is empty (onboarding) or the submitted model already occupies it * Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots) * Revert "Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots)" This reverts commit 2376aa50ccb0f170e5412a14c5aee33798ca1f06. * Fix: VLM connectivity probe never found the local test image - the gateway adapter's relative dirname chain resolved two levels short of the package root, so every probe silently fell back to a public URL that is unreachable in offline deployments. Anchor both probe copies on nexent.__file__ so the path survives module moves. --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * cherry-pick: HITL bugfixes from PR #3948 into hotfix/v2.6.1 (#3959) * Bubfix: guarantee event order, harden chunk buffer, SSE-subscribe controller, and break adapter on terminal human_run (#3948) * fix(hitl): preserve correct event ordering between observer chunks and human_interaction requests Root cause: the worker thread writes human_interaction events synchronously via SQLAlchemy in ask_user, while model_output_thinking/parse observer messages flow through the async consumer and are flushed only on a batched threshold (32 chunks or 250ms). When the worker suspends before that flush fires, human_interaction gains a lower event_seq number than the already-buffered observer chunks, causing the SSE replay stream to show them in the wrong order. Fix: replace the plain async-for consumer loop with a manual asyncio.wait iterator using a 50ms timeout. Once the worker finishes producing model output (i.e. right before ask_user), the loop times out and flushes any buffered observer chunks to the DB first, guaranteeing they precede the subsequent human_interaction row. Empty queue idle periods are essentially zero-cost; overall DB write frequency stays on par with the original. * fix(hitl): guarantee observer chunks precede human_interaction in DB event order When the agent invokes ask_user, two independent write paths caused the human_interaction row to be persisted BEFORE model_output_thinking / parse chunks, breaking the SSE replay ordering: the worker thread writes HITL events synchronously via SQLAlchemy, while observer messages flow through the async consumer which only flushes on a batched threshold. Fix: introduce a thread-safe shared chunk buffer on RuntimeInteractionPort (port.add_chunk / port.take_chunks). The async consumer pushes every processed chunk there; the worker thread calls flush_chunks_until_idle() before dispatching any HITL event — it polls the shared buffer and waits for the async loop to drain the observer queue (20ms idle window, 500ms max wait), then persists every chunk in its own transaction. This guarantees chunk event_seq < human_interaction event_seq regardless of async scheduling latency. Also fix ImportError: openai 2.50 removed the httpx2 module. OpenAIModel now falls back from httpx2 to httpx at import time. * test(hitl): cover shared chunk buffer, flush_chunks_until_idle, and httpx2-fallback paths Add unit tests for the RuntimeInteractionPort thread-safe chunk buffer and the flush_chunks_until_idle poll loop that guarantees observer chunks precede human_interaction events in DB order. All five HITL entry points (dispatch / boundary / receipt / finish / _wait_until_ready) are verified to invoke the idle flush before opening their transaction. Also add two tests for the openai_llm httpx2 → httpx ImportError fallback introduced to support openai >= 2.50 where the httpx2 shim was removed: one covers the fallback path, one confirms httpx2 still wins when present. * fix(hitl): address 4 review comments — hard deadline, emit_in_flight, peek_chunks, try/except safety Fix 4 real issues flagged by github-code-review: 1. Non-resettable hard_deadline in flush_chunks_until_idle — previously reset on every drain, meaning a model that kept producing chunks could stall the worker forever. deadline is now computed once at entry and the sleep call clips to hard_deadline - now. 2. _emit_in_flight Event bridges the async emit path and the worker's idle poll. Without this, buffer-empty = 'persisted' was confused with buffer-empty = 'taken for emit but still in run_blocking queue'. The worker now checks both 'buffer empty for settle_ms' AND 'no emit in flight' before deciding the async side is truly idle. 3. peek_chunks() replaces the take-put-back pattern in _flush_if_due. Previously the async loop drained the buffer, decided it was not yet due, then put everything back. That transiently-empty window (16 us normally, arbitrarily long under GIL/GC/preemption) was enough for the worker's 20 ms poll to mis-fire. We now peek (read count, no drain) and only take_chunks when we actually intend to persist. 4. emit_chunks wrapped in try/except that puts drained chunks back into the shared buffer before re-raising, and finish() wraps its flush call in try/except: pass. Guarantees (a) no chunk loss on DB failure and (b) the terminal human_run row is always written even if the flush step fails. Tests added: - 12 pure-mock unit tests in test_runtime_port_chunk_buffer.py cover hard_deadline, _emit_in_flight, peek_chunks, begin_emit/end_emit, try/except path, and every HITL entry-point's flush-before-transaction. - 1 async execute_attempt integration test in new test_application_execute_attempt.py drives the full consumer loop through _flush_if_due (peek → take → begin/end_emit) and the final flush, verifying that every patch line added in application.py is hit. * perf(hitl): stop polling while SSE stream is active, fallback to 5s when disconnected When isRunning=true the EventSource already pushes human_interaction and human_execution events in real time — the 1.5s polling loop duplicated that work, hitting the DB and re-rendering the frontend for every tick. Disable polling entirely while the SSE stream is alive, and drop to 5s intervals only when the stream is closed (e.g. page load before the first run, or after a run finishes) so we can still discover WAITING_HUMAN requests that were created while the client was disconnected. Add isRunning to the useEffect dependency array so the polling cadence resets immediately when the SSE connection state changes. * perf(hitl): replace polling with SSE subscription and move snapshot off the write path Frontend — /conversation polling → /{run_id}/events SSE: - Replace the 5s conversation snapshot polling with a native EventSource subscription to the backend's /{run_id}/events SSE stream. Discovery is now one-shot: conversationId change and the agent stream pause (isRunning true→false), the exact moment a HITL run is most likely to exist. The SSE stream then keeps run state live with native auto-reconnect. - Add dual guards inside refresh() to absorb the thundering herd from adapter.onHumanInteractionEvent (fires once per HITL SSE chunk) plus our own SSE effect: (1) in-flight dedupe — one snapshot absorbs all concurrent callers and returns cached state; (2) 3s minimum interval so bursts after the in-flight resolves do not immediately re-hit DB. - Use a runRef mirror so refresh() stays stable and downstream effects do not re-run on every snapshot. - Detect terminal status inside SSE onmessage and proactively es.close() to prevent EventSource from reconnecting forever against COMPLETED runs. Backend — snapshot off the write path: - Add repository.read_only() context manager: plain SELECT without WITH FOR UPDATE, no transaction, no flush, no _expire scan. Pure reads must not contend with worker writes on the same row lock. - Add service.light_snapshot() using read_only. Retain snapshot() as a writer-path API for any future lock-held callers. - Route conversation_snapshot, run snapshot endpoint, and both snapshot calls inside stream_run() through light_snapshot. - Move expiration to the writer path: decide() still calls _expire inline before processing each request, and expire_waiting() remains the periodic scheduler sweep. Impact: conversation snapshot calls drop from 12+/min (polling) or 10+/s (burst from adapter + SSE) to at most one every 3s. Each call is now two plain SELECTs instead of a lock-held transaction with a possible write from _expire. Read and write paths are fully decoupled. * fix(hitl): detect terminal human_run in adapter and break stream so isRunning flips false After a HITL run reaches FAILED/COMPLETED, Assistant-UI's isRunning stayed true — the stop button remained visible and new messages went into the queue buffer instead of being sent normally. The root cause is that isRunning is driven entirely by the ChatModelRun generator lifetime, which only returns when the backend SSE HTTP connection closes (reader.read() -> done=true). The backend stream_run loop can hang on heartbeat even after the run is terminal when the SSE was opened during WAITING_HUMAN with attempt_active=true: the break condition requires both cursor >= event_seq AND (terminal status OR WAITING_HUMAN with attempt_active=false and empty rows). If continueHitl fires mid-flight with a stale after_event, the cursor never catches up, so the SSE stays alive forever and the generator never returns. Stop depending on the backend closing first. Inside the adapter's SSE chunk loop, detect a terminal human_run event (status in COMPLETED, FAILED, STOPPED, EXPIRED), set a hitlTerminal flag, break the inner for-loop, and let the outer while-loop exit via the same flag on the next iteration. Assistant-UI sees the generator return and flips isRunning false immediately. Only affects HITL streams — the normal non-HITL agent path never emits human_run events so this branch is never taken. * test(hitl): update mock from snapshot to light_snapshot after read-path refactor test_human_interaction_app.py still mocked service.snapshot after commit 288ae4e69 moved conversation_snapshot and the run snapshot endpoint to service.light_snapshot (read-only path, no lock, no _expire). The fixture return_value and the two assert_called_once_with/assert_not_called assertions all referenced the old method name, causing CI to fail because MagicMock.snapshot was never called. * test(hitl): raise diff coverage above the 90% merge gate Codecov reported 70.43% patch coverage (target 90%) because new error and race paths in the HITL changes had no tests. Add mocked unit tests for: leftover chunk flush in execute_attempt's finally block before the failed finish, CancelledError scope and stop-event fallbacks, RunTerminated finish race, recovery-required outcome, and chunk iterator aclose failure tolerance; runtime_port in-flight emit busy detection, chunk restoration when emit_chunks raises, and terminal status persistence on flush failure; light_snapshot/read_only service behavior with signed tenant and user scoping; and the httpx fallback when openai._base_client.httpx2 is absent. Measured locally with CI-equivalent per-file pytest isolation: patch coverage 202/202 = 100%. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * refactor(hitl-test): dedupe fake port setup to satisfy SonarCloud duplication gate SonarCloud failed the quality gate with new_duplicated_lines_density=5.2% (threshold 3%), caused solely by test_application_execute_attempt.py: the inline _Port stub in the flush test and the one in _run_execute_attempt duplicated ~69 lines (2 CPD blocks, 14.4% file density). Extract a shared _build_port_class/_make_port_factory plus a _patched_application context manager and _execute_attempt_args so both call sites reuse a single definition; drop dead code (last_flush, install/monkeypatches, unused imports) and fix the latent bare-contextmanager NameError by using contextlib.contextmanager. No behavioral change; all 8 tests pass. * fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm openai >= 2.50 removed the httpx2 shim from openai._base_client. The bare import httpx2 causes ImportError in CI and on systems with recent openai versions. This restores the try/except fallback introduced in PR #3948 commit 9521b934 and later accidentally reverted by commit d086da259. * Revert "fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm" This reverts commit 8e06ce348dfc8c34baf42c6bfa8211883d0a2c61. * 🐛 Bugfix: Fixed an issue where the sandbox container user lacked the permissions to create folders and files. (#3963) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngi… (#3962) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngineProvider - get_provider_models routed every provider through the OpenAI-compatible adapter, so ModelEngine batch import failed (wrong endpoint path /open/router/v1/models, self-signed cert, custom type taxonomy, missing per-model base_url). The dedicated class existed but was never wired in. * chore: ModelEngine catalog base_url placeholder - preset public URL is wrong for private deployments, placeholder communicates the required /open/router/v1 path format --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * [codex] fix(agent): silently retry transient model errors (#3965) * fix(agent): retry transient model failures silently * fix(model): support OpenAI httpx2 timeout client * test(model): add deterministic OpenAI-compatible mock * fix(agent): keep stream runtime within line budget * Fix: AIDP knowledge base bug fix (#3967) * Fix: editing a ModelEngine model no longer flips ssl_verify to True - the update path only checked api_key emptiness while the create path also exempts open/router URLs (ModelEngine self-signed certs). The edit dialog prefills the real key and always submits it, so any edit silently broke connectivity. Exemption now checks the payload URL with a fallback to the stored record; batch-edit groups get the same protection * refactor: extract MODEL_ENGINE_URL_MARKER constant (SonarCloud S1192) and use a placeholder domain in test fixtures - no behavior change * [fix] enforce explicit CodeAgent termination and silent recovery (#3969) * fix(agent): enforce explicit CodeAgent termination * fix(test): restore CodeAgent CI compatibility * cherry-pick: HITL reliability fixes from PR #3977 into hotfix/v2.6.1 (#3981) * Fix StopAsyncIteration leak in execute_attempt finally block Root cause: when the agent chunk stream exhausted normally, the finally block awaited the already-consumed anext_task, re-raising StopAsyncIteration which was not suppressed by the existing CancelledError handling. The leftover chunk flush was skipped, successful runs were marked as failed, and the claiming scheduler job logged errors. Fix: reset anext_task to None before breaking out of the consumption loop so the finally block skips the await and always reaches the leftover flush and terminal finish() write. Tightened the regression test to assert that a normally exhausted stream does not leak StopAsyncIteration and that finish() is called. Also deduplicated the two _Port stub classes via a shared factory to satisfy the SonarCloud new_duplicated_lines_density gate. * Fix HITL form not appearing until page refresh Root cause: the frontend discovery chain rate-limited every refresh() with a 3s min interval and in-flight coalescing, silently dropping the critical human_run/human_interaction events that follow an ask_user suspension. The run event stream goes quiet afterwards, so nothing re-triggered the snapshot and the form only appeared after a manual page reload. Fix: refresh() now takes a force flag that bypasses the throttle; force callers arriving while a snapshot is in flight are re-run via a trailing refreshRef invocation instead of being dropped. SSE human_run/human_interaction/human_decision/human_execution messages and the chat-adapter onHumanInteractionEvent callback now force refresh. * Fix SSE chunk/HITL event ordering race under real server load Root cause: chunk persistence and HITL event writes ran in two threads (async consumer via run_blocking on the control-io lane, worker thread synchronously) with seq assigned at DB row-lock acquisition time. Two race windows reordered messages on loaded servers but never locally: (1) flush_chunks_until_idle's 500ms hard deadline fired while the async drain was still in flight, so the HITL row committed before chunks produced earlier (form appearing before model output); (2) worker emit_chunks and the async _flush_if_due drained concurrently without mutual exclusion, so seq order followed lock acquisition instead of production order. Fix: replace the begin_emit/end_emit Event with a shared threading.Lock and move take_chunks+emit_chunks into one atomic critical section (drain_and_emit) used by both the async consumer and the worker flush. The hard deadline may now only fire once the lock is free, guaranteeing in-flight drains commit before the caller writes its HITL transaction. Added regressions: flush waiting for an in-flight drain past its deadline, and concurrent drains preserving chunk production order. * fix(hitl): recover pending form when SSE delivery stalls silently Root cause: form discovery relied solely on a single EventSource plus refresh() with no fallback. A half-open connection (e.g. hung dev proxy) never raises an error event or reconnects, so human_interaction events are lost until a manual page refresh. A hung snapshot fetch could also keep refreshInFlight stuck forever, silently dropping every later refresh, including forced ones. Changes: - Poll the read-only snapshot every 5s while a run is active; the tick shares the refresh throttle and in-flight guard, so it adds no load while SSE delivery is healthy and discovers a pending form within 5s when the stream stalls - Add a 15s AbortSignal timeout to human-interaction client requests so a hung fetch releases the in-flight guard instead of bricking it - Wrap the human_run chunk JSON.parse in the chat adapter with try/catch: the stream loop has a finally but no catch, so a malformed payload would silently kill the whole chat stream read loop * fix(hitl): stop parked human-input waits from consuming scheduler slots Root cause: while a run waits for a human decision, its executor task parks inside _wait_until_ready and the lease renewal loop keeps the lease alive, so the run occupies one HITL_MAX_CONCURRENCY slot for up to HITL_WAIT_SECONDS (default 24h). With HITL enabled every non-debug chat is dispatched through this scheduler, so two unattended forms filled the default concurrency of 2 and froze all conversations: new agent/run streams only emitted heartbeats because READY runs were never claimed. Fix: add LeaseScheduler.mark_waiting so executors can flag themselves as parked on external input. Slot capacity is now max_concurrency minus executing jobs only (running minus waiting), and the waiting flag is cleared in the job's finally block. RuntimeInteractionPort relays enter/exit of _wait_until_ready through a wait_reporter callback, covering resume, termination and lease loss paths. The reporter degrades safely: a stale SDK copy without mark_waiting falls back to slot-consuming waits, and call_soon_threadsafe is wrapped in a lambda because it does not forward keyword arguments. Config: raise the env example defaults from 24h to 1h waits and concurrency 2 to 100, since waiting runs no longer consume execution slots. Tests: new test_waiting_jobs_do_not_consume_concurrency; scheduler suite 14/14, HITL service 12 passed, runtime and app suites 43/43. * test(scheduler): fix flaky waiting-concurrency assertion on fast event loops Root cause: job 2's executor completed instantly after appending to started, so its done-callback could discard it from _running before the active_count == 2 assertion ran. On Linux CI the event loop schedules that callback first, making the test fail intermittently. Fix: both executors now park on the shared gate via separate running events, so the assertion observes a stable running set instead of a transient window. * Fix: drop unrelated AIDP interface refactor from the AIDP knowledge base fix (#3980) The AIDP knowledge base fix reached hotfix/v2.6.1 through PR #3967, which also carried two unrelated upstream changes that this release branch never had: - #3909 Knowledge base interface optimization (AIDP UI refactor) - #3930 support AIDP knowledge file deletion and download Both are removed here so the release line keeps only the bug fix. - Restore the AIDP frontend components to their pre-refactor layout and drop the helper modules only the refactor used: AidpKnowledgeBaseModalParts, useAidpGroupOptions, aidpUploadUtils. - Drop the #3930 document remove/download endpoints from services/api.ts and the AIDP translations that only those screens referenced. - Keep the fix itself unchanged: knowledge-base scoped Channels and KnowledgeFiles/History paths, all-status document listing, keyword search, status labels (UPLOADING / PROCESSING / EXTRACTING) and the upload-triggered polling. Verified: - pytest test/ext_components/aidp -q -> 583 passed - frontend `npm run type-check` (tsc --noEmit) -> no errors * fix(agent): accept reasoning-prefixed code actions (#3990) * refactor: remove human interaction features and related configurations (#3988) * refactor: remove human interaction features and related configurations * refactor: remove human interaction features and related configurations --------- Co-authored-by: cj2026-bit <647646783@qq.com> Co-authored-by: lijiayang619 <1170349871@qq.com> Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> Co-authored-by: bernard1234 <840646206@qq.com> Co-authored-by: panyehong <91180085+YehongPan@users.noreply.github.com> Co-authored-by: Jason Wang <56037774+JasonW404@users.noreply.github.com> Co-authored-by: gs-aion <gs597153711@qq.com> Co-authored-by: Dallas98 <40557804+Dallas98@users.noreply.github.com> Co-authored-by: chase <byzhangxin11@126.com> | 8 小时前 | |
🐛 Fix:In-Flight Knowledge-File and Knowledge-Base Deletion (#3842) * fix: coordinate in-flight knowledge file deletion * test: cover document deletion coordination * test: reduce coverage test duplication * fix: guard knowledge base deletion during ingestion * fix: finish knowledge-base deletion cleanup * fix: adapt knowledge-base deletion guard to management services * ci: exclude test fixtures from duplication gate * ci: document test fixture duplication exclusion * fix: preserve document deletion response messages * test: cover deletion response and cancellation branches * fix: propagate knowledge-base deletion errors * chore: remove unrelated Sonar config changes * chore: align Sonar config with develop | 15 天前 | |
fix: harden quota, tag, model and API-key contracts found by daily test suite (#4002) - quota: reject negative GB/MB inputs and warning>=critical threshold pairs (previously persisted as 200 with semantically inverted config) - api-key: mask access_key in /api-keys and /user/tokens list responses; only create/refresh may return the full secret once - tag: translate the DB trigger's 'Tag assignment limit exceeded' (without the 'Resource ' prefix) into a structured 409, and flush replacement deletes before inserts so a full tag replacement no longer trips the assignment-capacity trigger - model: propagate ValueError from create/batch_create so duplicate display names map to 409 and malformed batch entries to 422 instead of 500; include model_type in /model/llm_list items; tolerate quick-config entries without model_repo in get_model_name_from_config - test: align model service tests with the ValueError passthrough contract Co-authored-by: chase <byzhangxin11@126.com> | 5 小时前 | |
merge(develop): merge v2.6.1 hotfixes from hotfix/v2.6.1 (#3998) * 🐛 Fix(evaluation): run trials in runtime service (#3954) * fix(evaluation): run trials in runtime service Route trial evaluations through the authenticated Config-to-Runtime proxy and use Config's manager only for creation-stage preparation. Keep Agent execution and evaluator scoring in Runtime. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub config thread manager Keep pure-logic service import tests aligned with the Config and Runtime thread-manager split. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub runtime jwt helper * test(evaluation): cover trial proxy error paths * Fix/override delete (#3958) * Fix: override dialog only shows override values, not model defaults (deleted params no longer reappear) * Fix: custom param deletion persists (null markers), per-agent capacity overrides take effect, and edit-dialog connectivity probe uses stored api_key * Fix: rename ModelRequest.model_id to probe_model_id - model_dump() is spread into INSERT column lists, so a model_id field injected an explicit NULL primary key and broke model creation * Fix: move probe_model_id to a dedicated ModelProbeRequest subclass - ModelRequest.model_dump() is spread into INSERT column lists, so any non-column field breaks model creation (Unconsumed column names) * Fix: editing/adding a model no longer steals the occupied default-model slot - persistCustomLocalConfig now only writes the slot when it is empty (onboarding) or the submitted model already occupies it * Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots) * Revert "Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots)" This reverts commit 2376aa50ccb0f170e5412a14c5aee33798ca1f06. * Fix: VLM connectivity probe never found the local test image - the gateway adapter's relative dirname chain resolved two levels short of the package root, so every probe silently fell back to a public URL that is unreachable in offline deployments. Anchor both probe copies on nexent.__file__ so the path survives module moves. --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * cherry-pick: HITL bugfixes from PR #3948 into hotfix/v2.6.1 (#3959) * Bubfix: guarantee event order, harden chunk buffer, SSE-subscribe controller, and break adapter on terminal human_run (#3948) * fix(hitl): preserve correct event ordering between observer chunks and human_interaction requests Root cause: the worker thread writes human_interaction events synchronously via SQLAlchemy in ask_user, while model_output_thinking/parse observer messages flow through the async consumer and are flushed only on a batched threshold (32 chunks or 250ms). When the worker suspends before that flush fires, human_interaction gains a lower event_seq number than the already-buffered observer chunks, causing the SSE replay stream to show them in the wrong order. Fix: replace the plain async-for consumer loop with a manual asyncio.wait iterator using a 50ms timeout. Once the worker finishes producing model output (i.e. right before ask_user), the loop times out and flushes any buffered observer chunks to the DB first, guaranteeing they precede the subsequent human_interaction row. Empty queue idle periods are essentially zero-cost; overall DB write frequency stays on par with the original. * fix(hitl): guarantee observer chunks precede human_interaction in DB event order When the agent invokes ask_user, two independent write paths caused the human_interaction row to be persisted BEFORE model_output_thinking / parse chunks, breaking the SSE replay ordering: the worker thread writes HITL events synchronously via SQLAlchemy, while observer messages flow through the async consumer which only flushes on a batched threshold. Fix: introduce a thread-safe shared chunk buffer on RuntimeInteractionPort (port.add_chunk / port.take_chunks). The async consumer pushes every processed chunk there; the worker thread calls flush_chunks_until_idle() before dispatching any HITL event — it polls the shared buffer and waits for the async loop to drain the observer queue (20ms idle window, 500ms max wait), then persists every chunk in its own transaction. This guarantees chunk event_seq < human_interaction event_seq regardless of async scheduling latency. Also fix ImportError: openai 2.50 removed the httpx2 module. OpenAIModel now falls back from httpx2 to httpx at import time. * test(hitl): cover shared chunk buffer, flush_chunks_until_idle, and httpx2-fallback paths Add unit tests for the RuntimeInteractionPort thread-safe chunk buffer and the flush_chunks_until_idle poll loop that guarantees observer chunks precede human_interaction events in DB order. All five HITL entry points (dispatch / boundary / receipt / finish / _wait_until_ready) are verified to invoke the idle flush before opening their transaction. Also add two tests for the openai_llm httpx2 → httpx ImportError fallback introduced to support openai >= 2.50 where the httpx2 shim was removed: one covers the fallback path, one confirms httpx2 still wins when present. * fix(hitl): address 4 review comments — hard deadline, emit_in_flight, peek_chunks, try/except safety Fix 4 real issues flagged by github-code-review: 1. Non-resettable hard_deadline in flush_chunks_until_idle — previously reset on every drain, meaning a model that kept producing chunks could stall the worker forever. deadline is now computed once at entry and the sleep call clips to hard_deadline - now. 2. _emit_in_flight Event bridges the async emit path and the worker's idle poll. Without this, buffer-empty = 'persisted' was confused with buffer-empty = 'taken for emit but still in run_blocking queue'. The worker now checks both 'buffer empty for settle_ms' AND 'no emit in flight' before deciding the async side is truly idle. 3. peek_chunks() replaces the take-put-back pattern in _flush_if_due. Previously the async loop drained the buffer, decided it was not yet due, then put everything back. That transiently-empty window (16 us normally, arbitrarily long under GIL/GC/preemption) was enough for the worker's 20 ms poll to mis-fire. We now peek (read count, no drain) and only take_chunks when we actually intend to persist. 4. emit_chunks wrapped in try/except that puts drained chunks back into the shared buffer before re-raising, and finish() wraps its flush call in try/except: pass. Guarantees (a) no chunk loss on DB failure and (b) the terminal human_run row is always written even if the flush step fails. Tests added: - 12 pure-mock unit tests in test_runtime_port_chunk_buffer.py cover hard_deadline, _emit_in_flight, peek_chunks, begin_emit/end_emit, try/except path, and every HITL entry-point's flush-before-transaction. - 1 async execute_attempt integration test in new test_application_execute_attempt.py drives the full consumer loop through _flush_if_due (peek → take → begin/end_emit) and the final flush, verifying that every patch line added in application.py is hit. * perf(hitl): stop polling while SSE stream is active, fallback to 5s when disconnected When isRunning=true the EventSource already pushes human_interaction and human_execution events in real time — the 1.5s polling loop duplicated that work, hitting the DB and re-rendering the frontend for every tick. Disable polling entirely while the SSE stream is alive, and drop to 5s intervals only when the stream is closed (e.g. page load before the first run, or after a run finishes) so we can still discover WAITING_HUMAN requests that were created while the client was disconnected. Add isRunning to the useEffect dependency array so the polling cadence resets immediately when the SSE connection state changes. * perf(hitl): replace polling with SSE subscription and move snapshot off the write path Frontend — /conversation polling → /{run_id}/events SSE: - Replace the 5s conversation snapshot polling with a native EventSource subscription to the backend's /{run_id}/events SSE stream. Discovery is now one-shot: conversationId change and the agent stream pause (isRunning true→false), the exact moment a HITL run is most likely to exist. The SSE stream then keeps run state live with native auto-reconnect. - Add dual guards inside refresh() to absorb the thundering herd from adapter.onHumanInteractionEvent (fires once per HITL SSE chunk) plus our own SSE effect: (1) in-flight dedupe — one snapshot absorbs all concurrent callers and returns cached state; (2) 3s minimum interval so bursts after the in-flight resolves do not immediately re-hit DB. - Use a runRef mirror so refresh() stays stable and downstream effects do not re-run on every snapshot. - Detect terminal status inside SSE onmessage and proactively es.close() to prevent EventSource from reconnecting forever against COMPLETED runs. Backend — snapshot off the write path: - Add repository.read_only() context manager: plain SELECT without WITH FOR UPDATE, no transaction, no flush, no _expire scan. Pure reads must not contend with worker writes on the same row lock. - Add service.light_snapshot() using read_only. Retain snapshot() as a writer-path API for any future lock-held callers. - Route conversation_snapshot, run snapshot endpoint, and both snapshot calls inside stream_run() through light_snapshot. - Move expiration to the writer path: decide() still calls _expire inline before processing each request, and expire_waiting() remains the periodic scheduler sweep. Impact: conversation snapshot calls drop from 12+/min (polling) or 10+/s (burst from adapter + SSE) to at most one every 3s. Each call is now two plain SELECTs instead of a lock-held transaction with a possible write from _expire. Read and write paths are fully decoupled. * fix(hitl): detect terminal human_run in adapter and break stream so isRunning flips false After a HITL run reaches FAILED/COMPLETED, Assistant-UI's isRunning stayed true — the stop button remained visible and new messages went into the queue buffer instead of being sent normally. The root cause is that isRunning is driven entirely by the ChatModelRun generator lifetime, which only returns when the backend SSE HTTP connection closes (reader.read() -> done=true). The backend stream_run loop can hang on heartbeat even after the run is terminal when the SSE was opened during WAITING_HUMAN with attempt_active=true: the break condition requires both cursor >= event_seq AND (terminal status OR WAITING_HUMAN with attempt_active=false and empty rows). If continueHitl fires mid-flight with a stale after_event, the cursor never catches up, so the SSE stays alive forever and the generator never returns. Stop depending on the backend closing first. Inside the adapter's SSE chunk loop, detect a terminal human_run event (status in COMPLETED, FAILED, STOPPED, EXPIRED), set a hitlTerminal flag, break the inner for-loop, and let the outer while-loop exit via the same flag on the next iteration. Assistant-UI sees the generator return and flips isRunning false immediately. Only affects HITL streams — the normal non-HITL agent path never emits human_run events so this branch is never taken. * test(hitl): update mock from snapshot to light_snapshot after read-path refactor test_human_interaction_app.py still mocked service.snapshot after commit 288ae4e69 moved conversation_snapshot and the run snapshot endpoint to service.light_snapshot (read-only path, no lock, no _expire). The fixture return_value and the two assert_called_once_with/assert_not_called assertions all referenced the old method name, causing CI to fail because MagicMock.snapshot was never called. * test(hitl): raise diff coverage above the 90% merge gate Codecov reported 70.43% patch coverage (target 90%) because new error and race paths in the HITL changes had no tests. Add mocked unit tests for: leftover chunk flush in execute_attempt's finally block before the failed finish, CancelledError scope and stop-event fallbacks, RunTerminated finish race, recovery-required outcome, and chunk iterator aclose failure tolerance; runtime_port in-flight emit busy detection, chunk restoration when emit_chunks raises, and terminal status persistence on flush failure; light_snapshot/read_only service behavior with signed tenant and user scoping; and the httpx fallback when openai._base_client.httpx2 is absent. Measured locally with CI-equivalent per-file pytest isolation: patch coverage 202/202 = 100%. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * refactor(hitl-test): dedupe fake port setup to satisfy SonarCloud duplication gate SonarCloud failed the quality gate with new_duplicated_lines_density=5.2% (threshold 3%), caused solely by test_application_execute_attempt.py: the inline _Port stub in the flush test and the one in _run_execute_attempt duplicated ~69 lines (2 CPD blocks, 14.4% file density). Extract a shared _build_port_class/_make_port_factory plus a _patched_application context manager and _execute_attempt_args so both call sites reuse a single definition; drop dead code (last_flush, install/monkeypatches, unused imports) and fix the latent bare-contextmanager NameError by using contextlib.contextmanager. No behavioral change; all 8 tests pass. * fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm openai >= 2.50 removed the httpx2 shim from openai._base_client. The bare import httpx2 causes ImportError in CI and on systems with recent openai versions. This restores the try/except fallback introduced in PR #3948 commit 9521b934 and later accidentally reverted by commit d086da259. * Revert "fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm" This reverts commit 8e06ce348dfc8c34baf42c6bfa8211883d0a2c61. * 🐛 Bugfix: Fixed an issue where the sandbox container user lacked the permissions to create folders and files. (#3963) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngi… (#3962) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngineProvider - get_provider_models routed every provider through the OpenAI-compatible adapter, so ModelEngine batch import failed (wrong endpoint path /open/router/v1/models, self-signed cert, custom type taxonomy, missing per-model base_url). The dedicated class existed but was never wired in. * chore: ModelEngine catalog base_url placeholder - preset public URL is wrong for private deployments, placeholder communicates the required /open/router/v1 path format --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * [codex] fix(agent): silently retry transient model errors (#3965) * fix(agent): retry transient model failures silently * fix(model): support OpenAI httpx2 timeout client * test(model): add deterministic OpenAI-compatible mock * fix(agent): keep stream runtime within line budget * Fix: AIDP knowledge base bug fix (#3967) * Fix: editing a ModelEngine model no longer flips ssl_verify to True - the update path only checked api_key emptiness while the create path also exempts open/router URLs (ModelEngine self-signed certs). The edit dialog prefills the real key and always submits it, so any edit silently broke connectivity. Exemption now checks the payload URL with a fallback to the stored record; batch-edit groups get the same protection * refactor: extract MODEL_ENGINE_URL_MARKER constant (SonarCloud S1192) and use a placeholder domain in test fixtures - no behavior change * [fix] enforce explicit CodeAgent termination and silent recovery (#3969) * fix(agent): enforce explicit CodeAgent termination * fix(test): restore CodeAgent CI compatibility * cherry-pick: HITL reliability fixes from PR #3977 into hotfix/v2.6.1 (#3981) * Fix StopAsyncIteration leak in execute_attempt finally block Root cause: when the agent chunk stream exhausted normally, the finally block awaited the already-consumed anext_task, re-raising StopAsyncIteration which was not suppressed by the existing CancelledError handling. The leftover chunk flush was skipped, successful runs were marked as failed, and the claiming scheduler job logged errors. Fix: reset anext_task to None before breaking out of the consumption loop so the finally block skips the await and always reaches the leftover flush and terminal finish() write. Tightened the regression test to assert that a normally exhausted stream does not leak StopAsyncIteration and that finish() is called. Also deduplicated the two _Port stub classes via a shared factory to satisfy the SonarCloud new_duplicated_lines_density gate. * Fix HITL form not appearing until page refresh Root cause: the frontend discovery chain rate-limited every refresh() with a 3s min interval and in-flight coalescing, silently dropping the critical human_run/human_interaction events that follow an ask_user suspension. The run event stream goes quiet afterwards, so nothing re-triggered the snapshot and the form only appeared after a manual page reload. Fix: refresh() now takes a force flag that bypasses the throttle; force callers arriving while a snapshot is in flight are re-run via a trailing refreshRef invocation instead of being dropped. SSE human_run/human_interaction/human_decision/human_execution messages and the chat-adapter onHumanInteractionEvent callback now force refresh. * Fix SSE chunk/HITL event ordering race under real server load Root cause: chunk persistence and HITL event writes ran in two threads (async consumer via run_blocking on the control-io lane, worker thread synchronously) with seq assigned at DB row-lock acquisition time. Two race windows reordered messages on loaded servers but never locally: (1) flush_chunks_until_idle's 500ms hard deadline fired while the async drain was still in flight, so the HITL row committed before chunks produced earlier (form appearing before model output); (2) worker emit_chunks and the async _flush_if_due drained concurrently without mutual exclusion, so seq order followed lock acquisition instead of production order. Fix: replace the begin_emit/end_emit Event with a shared threading.Lock and move take_chunks+emit_chunks into one atomic critical section (drain_and_emit) used by both the async consumer and the worker flush. The hard deadline may now only fire once the lock is free, guaranteeing in-flight drains commit before the caller writes its HITL transaction. Added regressions: flush waiting for an in-flight drain past its deadline, and concurrent drains preserving chunk production order. * fix(hitl): recover pending form when SSE delivery stalls silently Root cause: form discovery relied solely on a single EventSource plus refresh() with no fallback. A half-open connection (e.g. hung dev proxy) never raises an error event or reconnects, so human_interaction events are lost until a manual page refresh. A hung snapshot fetch could also keep refreshInFlight stuck forever, silently dropping every later refresh, including forced ones. Changes: - Poll the read-only snapshot every 5s while a run is active; the tick shares the refresh throttle and in-flight guard, so it adds no load while SSE delivery is healthy and discovers a pending form within 5s when the stream stalls - Add a 15s AbortSignal timeout to human-interaction client requests so a hung fetch releases the in-flight guard instead of bricking it - Wrap the human_run chunk JSON.parse in the chat adapter with try/catch: the stream loop has a finally but no catch, so a malformed payload would silently kill the whole chat stream read loop * fix(hitl): stop parked human-input waits from consuming scheduler slots Root cause: while a run waits for a human decision, its executor task parks inside _wait_until_ready and the lease renewal loop keeps the lease alive, so the run occupies one HITL_MAX_CONCURRENCY slot for up to HITL_WAIT_SECONDS (default 24h). With HITL enabled every non-debug chat is dispatched through this scheduler, so two unattended forms filled the default concurrency of 2 and froze all conversations: new agent/run streams only emitted heartbeats because READY runs were never claimed. Fix: add LeaseScheduler.mark_waiting so executors can flag themselves as parked on external input. Slot capacity is now max_concurrency minus executing jobs only (running minus waiting), and the waiting flag is cleared in the job's finally block. RuntimeInteractionPort relays enter/exit of _wait_until_ready through a wait_reporter callback, covering resume, termination and lease loss paths. The reporter degrades safely: a stale SDK copy without mark_waiting falls back to slot-consuming waits, and call_soon_threadsafe is wrapped in a lambda because it does not forward keyword arguments. Config: raise the env example defaults from 24h to 1h waits and concurrency 2 to 100, since waiting runs no longer consume execution slots. Tests: new test_waiting_jobs_do_not_consume_concurrency; scheduler suite 14/14, HITL service 12 passed, runtime and app suites 43/43. * test(scheduler): fix flaky waiting-concurrency assertion on fast event loops Root cause: job 2's executor completed instantly after appending to started, so its done-callback could discard it from _running before the active_count == 2 assertion ran. On Linux CI the event loop schedules that callback first, making the test fail intermittently. Fix: both executors now park on the shared gate via separate running events, so the assertion observes a stable running set instead of a transient window. * Fix: drop unrelated AIDP interface refactor from the AIDP knowledge base fix (#3980) The AIDP knowledge base fix reached hotfix/v2.6.1 through PR #3967, which also carried two unrelated upstream changes that this release branch never had: - #3909 Knowledge base interface optimization (AIDP UI refactor) - #3930 support AIDP knowledge file deletion and download Both are removed here so the release line keeps only the bug fix. - Restore the AIDP frontend components to their pre-refactor layout and drop the helper modules only the refactor used: AidpKnowledgeBaseModalParts, useAidpGroupOptions, aidpUploadUtils. - Drop the #3930 document remove/download endpoints from services/api.ts and the AIDP translations that only those screens referenced. - Keep the fix itself unchanged: knowledge-base scoped Channels and KnowledgeFiles/History paths, all-status document listing, keyword search, status labels (UPLOADING / PROCESSING / EXTRACTING) and the upload-triggered polling. Verified: - pytest test/ext_components/aidp -q -> 583 passed - frontend `npm run type-check` (tsc --noEmit) -> no errors * fix(agent): accept reasoning-prefixed code actions (#3990) * refactor: remove human interaction features and related configurations (#3988) * refactor: remove human interaction features and related configurations * refactor: remove human interaction features and related configurations --------- Co-authored-by: cj2026-bit <647646783@qq.com> Co-authored-by: lijiayang619 <1170349871@qq.com> Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> Co-authored-by: bernard1234 <840646206@qq.com> Co-authored-by: panyehong <91180085+YehongPan@users.noreply.github.com> Co-authored-by: Jason Wang <56037774+JasonW404@users.noreply.github.com> Co-authored-by: gs-aion <gs597153711@qq.com> Co-authored-by: Dallas98 <40557804+Dallas98@users.noreply.github.com> Co-authored-by: chase <byzhangxin11@126.com> | 8 小时前 | |
merge(develop): merge v2.6.1 hotfixes from hotfix/v2.6.1 (#3998) * 🐛 Fix(evaluation): run trials in runtime service (#3954) * fix(evaluation): run trials in runtime service Route trial evaluations through the authenticated Config-to-Runtime proxy and use Config's manager only for creation-stage preparation. Keep Agent execution and evaluator scoring in Runtime. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub config thread manager Keep pure-logic service import tests aligned with the Config and Runtime thread-manager split. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub runtime jwt helper * test(evaluation): cover trial proxy error paths * Fix/override delete (#3958) * Fix: override dialog only shows override values, not model defaults (deleted params no longer reappear) * Fix: custom param deletion persists (null markers), per-agent capacity overrides take effect, and edit-dialog connectivity probe uses stored api_key * Fix: rename ModelRequest.model_id to probe_model_id - model_dump() is spread into INSERT column lists, so a model_id field injected an explicit NULL primary key and broke model creation * Fix: move probe_model_id to a dedicated ModelProbeRequest subclass - ModelRequest.model_dump() is spread into INSERT column lists, so any non-column field breaks model creation (Unconsumed column names) * Fix: editing/adding a model no longer steals the occupied default-model slot - persistCustomLocalConfig now only writes the slot when it is empty (onboarding) or the submitted model already occupies it * Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots) * Revert "Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots)" This reverts commit 2376aa50ccb0f170e5412a14c5aee33798ca1f06. * Fix: VLM connectivity probe never found the local test image - the gateway adapter's relative dirname chain resolved two levels short of the package root, so every probe silently fell back to a public URL that is unreachable in offline deployments. Anchor both probe copies on nexent.__file__ so the path survives module moves. --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * cherry-pick: HITL bugfixes from PR #3948 into hotfix/v2.6.1 (#3959) * Bubfix: guarantee event order, harden chunk buffer, SSE-subscribe controller, and break adapter on terminal human_run (#3948) * fix(hitl): preserve correct event ordering between observer chunks and human_interaction requests Root cause: the worker thread writes human_interaction events synchronously via SQLAlchemy in ask_user, while model_output_thinking/parse observer messages flow through the async consumer and are flushed only on a batched threshold (32 chunks or 250ms). When the worker suspends before that flush fires, human_interaction gains a lower event_seq number than the already-buffered observer chunks, causing the SSE replay stream to show them in the wrong order. Fix: replace the plain async-for consumer loop with a manual asyncio.wait iterator using a 50ms timeout. Once the worker finishes producing model output (i.e. right before ask_user), the loop times out and flushes any buffered observer chunks to the DB first, guaranteeing they precede the subsequent human_interaction row. Empty queue idle periods are essentially zero-cost; overall DB write frequency stays on par with the original. * fix(hitl): guarantee observer chunks precede human_interaction in DB event order When the agent invokes ask_user, two independent write paths caused the human_interaction row to be persisted BEFORE model_output_thinking / parse chunks, breaking the SSE replay ordering: the worker thread writes HITL events synchronously via SQLAlchemy, while observer messages flow through the async consumer which only flushes on a batched threshold. Fix: introduce a thread-safe shared chunk buffer on RuntimeInteractionPort (port.add_chunk / port.take_chunks). The async consumer pushes every processed chunk there; the worker thread calls flush_chunks_until_idle() before dispatching any HITL event — it polls the shared buffer and waits for the async loop to drain the observer queue (20ms idle window, 500ms max wait), then persists every chunk in its own transaction. This guarantees chunk event_seq < human_interaction event_seq regardless of async scheduling latency. Also fix ImportError: openai 2.50 removed the httpx2 module. OpenAIModel now falls back from httpx2 to httpx at import time. * test(hitl): cover shared chunk buffer, flush_chunks_until_idle, and httpx2-fallback paths Add unit tests for the RuntimeInteractionPort thread-safe chunk buffer and the flush_chunks_until_idle poll loop that guarantees observer chunks precede human_interaction events in DB order. All five HITL entry points (dispatch / boundary / receipt / finish / _wait_until_ready) are verified to invoke the idle flush before opening their transaction. Also add two tests for the openai_llm httpx2 → httpx ImportError fallback introduced to support openai >= 2.50 where the httpx2 shim was removed: one covers the fallback path, one confirms httpx2 still wins when present. * fix(hitl): address 4 review comments — hard deadline, emit_in_flight, peek_chunks, try/except safety Fix 4 real issues flagged by github-code-review: 1. Non-resettable hard_deadline in flush_chunks_until_idle — previously reset on every drain, meaning a model that kept producing chunks could stall the worker forever. deadline is now computed once at entry and the sleep call clips to hard_deadline - now. 2. _emit_in_flight Event bridges the async emit path and the worker's idle poll. Without this, buffer-empty = 'persisted' was confused with buffer-empty = 'taken for emit but still in run_blocking queue'. The worker now checks both 'buffer empty for settle_ms' AND 'no emit in flight' before deciding the async side is truly idle. 3. peek_chunks() replaces the take-put-back pattern in _flush_if_due. Previously the async loop drained the buffer, decided it was not yet due, then put everything back. That transiently-empty window (16 us normally, arbitrarily long under GIL/GC/preemption) was enough for the worker's 20 ms poll to mis-fire. We now peek (read count, no drain) and only take_chunks when we actually intend to persist. 4. emit_chunks wrapped in try/except that puts drained chunks back into the shared buffer before re-raising, and finish() wraps its flush call in try/except: pass. Guarantees (a) no chunk loss on DB failure and (b) the terminal human_run row is always written even if the flush step fails. Tests added: - 12 pure-mock unit tests in test_runtime_port_chunk_buffer.py cover hard_deadline, _emit_in_flight, peek_chunks, begin_emit/end_emit, try/except path, and every HITL entry-point's flush-before-transaction. - 1 async execute_attempt integration test in new test_application_execute_attempt.py drives the full consumer loop through _flush_if_due (peek → take → begin/end_emit) and the final flush, verifying that every patch line added in application.py is hit. * perf(hitl): stop polling while SSE stream is active, fallback to 5s when disconnected When isRunning=true the EventSource already pushes human_interaction and human_execution events in real time — the 1.5s polling loop duplicated that work, hitting the DB and re-rendering the frontend for every tick. Disable polling entirely while the SSE stream is alive, and drop to 5s intervals only when the stream is closed (e.g. page load before the first run, or after a run finishes) so we can still discover WAITING_HUMAN requests that were created while the client was disconnected. Add isRunning to the useEffect dependency array so the polling cadence resets immediately when the SSE connection state changes. * perf(hitl): replace polling with SSE subscription and move snapshot off the write path Frontend — /conversation polling → /{run_id}/events SSE: - Replace the 5s conversation snapshot polling with a native EventSource subscription to the backend's /{run_id}/events SSE stream. Discovery is now one-shot: conversationId change and the agent stream pause (isRunning true→false), the exact moment a HITL run is most likely to exist. The SSE stream then keeps run state live with native auto-reconnect. - Add dual guards inside refresh() to absorb the thundering herd from adapter.onHumanInteractionEvent (fires once per HITL SSE chunk) plus our own SSE effect: (1) in-flight dedupe — one snapshot absorbs all concurrent callers and returns cached state; (2) 3s minimum interval so bursts after the in-flight resolves do not immediately re-hit DB. - Use a runRef mirror so refresh() stays stable and downstream effects do not re-run on every snapshot. - Detect terminal status inside SSE onmessage and proactively es.close() to prevent EventSource from reconnecting forever against COMPLETED runs. Backend — snapshot off the write path: - Add repository.read_only() context manager: plain SELECT without WITH FOR UPDATE, no transaction, no flush, no _expire scan. Pure reads must not contend with worker writes on the same row lock. - Add service.light_snapshot() using read_only. Retain snapshot() as a writer-path API for any future lock-held callers. - Route conversation_snapshot, run snapshot endpoint, and both snapshot calls inside stream_run() through light_snapshot. - Move expiration to the writer path: decide() still calls _expire inline before processing each request, and expire_waiting() remains the periodic scheduler sweep. Impact: conversation snapshot calls drop from 12+/min (polling) or 10+/s (burst from adapter + SSE) to at most one every 3s. Each call is now two plain SELECTs instead of a lock-held transaction with a possible write from _expire. Read and write paths are fully decoupled. * fix(hitl): detect terminal human_run in adapter and break stream so isRunning flips false After a HITL run reaches FAILED/COMPLETED, Assistant-UI's isRunning stayed true — the stop button remained visible and new messages went into the queue buffer instead of being sent normally. The root cause is that isRunning is driven entirely by the ChatModelRun generator lifetime, which only returns when the backend SSE HTTP connection closes (reader.read() -> done=true). The backend stream_run loop can hang on heartbeat even after the run is terminal when the SSE was opened during WAITING_HUMAN with attempt_active=true: the break condition requires both cursor >= event_seq AND (terminal status OR WAITING_HUMAN with attempt_active=false and empty rows). If continueHitl fires mid-flight with a stale after_event, the cursor never catches up, so the SSE stays alive forever and the generator never returns. Stop depending on the backend closing first. Inside the adapter's SSE chunk loop, detect a terminal human_run event (status in COMPLETED, FAILED, STOPPED, EXPIRED), set a hitlTerminal flag, break the inner for-loop, and let the outer while-loop exit via the same flag on the next iteration. Assistant-UI sees the generator return and flips isRunning false immediately. Only affects HITL streams — the normal non-HITL agent path never emits human_run events so this branch is never taken. * test(hitl): update mock from snapshot to light_snapshot after read-path refactor test_human_interaction_app.py still mocked service.snapshot after commit 288ae4e69 moved conversation_snapshot and the run snapshot endpoint to service.light_snapshot (read-only path, no lock, no _expire). The fixture return_value and the two assert_called_once_with/assert_not_called assertions all referenced the old method name, causing CI to fail because MagicMock.snapshot was never called. * test(hitl): raise diff coverage above the 90% merge gate Codecov reported 70.43% patch coverage (target 90%) because new error and race paths in the HITL changes had no tests. Add mocked unit tests for: leftover chunk flush in execute_attempt's finally block before the failed finish, CancelledError scope and stop-event fallbacks, RunTerminated finish race, recovery-required outcome, and chunk iterator aclose failure tolerance; runtime_port in-flight emit busy detection, chunk restoration when emit_chunks raises, and terminal status persistence on flush failure; light_snapshot/read_only service behavior with signed tenant and user scoping; and the httpx fallback when openai._base_client.httpx2 is absent. Measured locally with CI-equivalent per-file pytest isolation: patch coverage 202/202 = 100%. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * refactor(hitl-test): dedupe fake port setup to satisfy SonarCloud duplication gate SonarCloud failed the quality gate with new_duplicated_lines_density=5.2% (threshold 3%), caused solely by test_application_execute_attempt.py: the inline _Port stub in the flush test and the one in _run_execute_attempt duplicated ~69 lines (2 CPD blocks, 14.4% file density). Extract a shared _build_port_class/_make_port_factory plus a _patched_application context manager and _execute_attempt_args so both call sites reuse a single definition; drop dead code (last_flush, install/monkeypatches, unused imports) and fix the latent bare-contextmanager NameError by using contextlib.contextmanager. No behavioral change; all 8 tests pass. * fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm openai >= 2.50 removed the httpx2 shim from openai._base_client. The bare import httpx2 causes ImportError in CI and on systems with recent openai versions. This restores the try/except fallback introduced in PR #3948 commit 9521b934 and later accidentally reverted by commit d086da259. * Revert "fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm" This reverts commit 8e06ce348dfc8c34baf42c6bfa8211883d0a2c61. * 🐛 Bugfix: Fixed an issue where the sandbox container user lacked the permissions to create folders and files. (#3963) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngi… (#3962) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngineProvider - get_provider_models routed every provider through the OpenAI-compatible adapter, so ModelEngine batch import failed (wrong endpoint path /open/router/v1/models, self-signed cert, custom type taxonomy, missing per-model base_url). The dedicated class existed but was never wired in. * chore: ModelEngine catalog base_url placeholder - preset public URL is wrong for private deployments, placeholder communicates the required /open/router/v1 path format --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * [codex] fix(agent): silently retry transient model errors (#3965) * fix(agent): retry transient model failures silently * fix(model): support OpenAI httpx2 timeout client * test(model): add deterministic OpenAI-compatible mock * fix(agent): keep stream runtime within line budget * Fix: AIDP knowledge base bug fix (#3967) * Fix: editing a ModelEngine model no longer flips ssl_verify to True - the update path only checked api_key emptiness while the create path also exempts open/router URLs (ModelEngine self-signed certs). The edit dialog prefills the real key and always submits it, so any edit silently broke connectivity. Exemption now checks the payload URL with a fallback to the stored record; batch-edit groups get the same protection * refactor: extract MODEL_ENGINE_URL_MARKER constant (SonarCloud S1192) and use a placeholder domain in test fixtures - no behavior change * [fix] enforce explicit CodeAgent termination and silent recovery (#3969) * fix(agent): enforce explicit CodeAgent termination * fix(test): restore CodeAgent CI compatibility * cherry-pick: HITL reliability fixes from PR #3977 into hotfix/v2.6.1 (#3981) * Fix StopAsyncIteration leak in execute_attempt finally block Root cause: when the agent chunk stream exhausted normally, the finally block awaited the already-consumed anext_task, re-raising StopAsyncIteration which was not suppressed by the existing CancelledError handling. The leftover chunk flush was skipped, successful runs were marked as failed, and the claiming scheduler job logged errors. Fix: reset anext_task to None before breaking out of the consumption loop so the finally block skips the await and always reaches the leftover flush and terminal finish() write. Tightened the regression test to assert that a normally exhausted stream does not leak StopAsyncIteration and that finish() is called. Also deduplicated the two _Port stub classes via a shared factory to satisfy the SonarCloud new_duplicated_lines_density gate. * Fix HITL form not appearing until page refresh Root cause: the frontend discovery chain rate-limited every refresh() with a 3s min interval and in-flight coalescing, silently dropping the critical human_run/human_interaction events that follow an ask_user suspension. The run event stream goes quiet afterwards, so nothing re-triggered the snapshot and the form only appeared after a manual page reload. Fix: refresh() now takes a force flag that bypasses the throttle; force callers arriving while a snapshot is in flight are re-run via a trailing refreshRef invocation instead of being dropped. SSE human_run/human_interaction/human_decision/human_execution messages and the chat-adapter onHumanInteractionEvent callback now force refresh. * Fix SSE chunk/HITL event ordering race under real server load Root cause: chunk persistence and HITL event writes ran in two threads (async consumer via run_blocking on the control-io lane, worker thread synchronously) with seq assigned at DB row-lock acquisition time. Two race windows reordered messages on loaded servers but never locally: (1) flush_chunks_until_idle's 500ms hard deadline fired while the async drain was still in flight, so the HITL row committed before chunks produced earlier (form appearing before model output); (2) worker emit_chunks and the async _flush_if_due drained concurrently without mutual exclusion, so seq order followed lock acquisition instead of production order. Fix: replace the begin_emit/end_emit Event with a shared threading.Lock and move take_chunks+emit_chunks into one atomic critical section (drain_and_emit) used by both the async consumer and the worker flush. The hard deadline may now only fire once the lock is free, guaranteeing in-flight drains commit before the caller writes its HITL transaction. Added regressions: flush waiting for an in-flight drain past its deadline, and concurrent drains preserving chunk production order. * fix(hitl): recover pending form when SSE delivery stalls silently Root cause: form discovery relied solely on a single EventSource plus refresh() with no fallback. A half-open connection (e.g. hung dev proxy) never raises an error event or reconnects, so human_interaction events are lost until a manual page refresh. A hung snapshot fetch could also keep refreshInFlight stuck forever, silently dropping every later refresh, including forced ones. Changes: - Poll the read-only snapshot every 5s while a run is active; the tick shares the refresh throttle and in-flight guard, so it adds no load while SSE delivery is healthy and discovers a pending form within 5s when the stream stalls - Add a 15s AbortSignal timeout to human-interaction client requests so a hung fetch releases the in-flight guard instead of bricking it - Wrap the human_run chunk JSON.parse in the chat adapter with try/catch: the stream loop has a finally but no catch, so a malformed payload would silently kill the whole chat stream read loop * fix(hitl): stop parked human-input waits from consuming scheduler slots Root cause: while a run waits for a human decision, its executor task parks inside _wait_until_ready and the lease renewal loop keeps the lease alive, so the run occupies one HITL_MAX_CONCURRENCY slot for up to HITL_WAIT_SECONDS (default 24h). With HITL enabled every non-debug chat is dispatched through this scheduler, so two unattended forms filled the default concurrency of 2 and froze all conversations: new agent/run streams only emitted heartbeats because READY runs were never claimed. Fix: add LeaseScheduler.mark_waiting so executors can flag themselves as parked on external input. Slot capacity is now max_concurrency minus executing jobs only (running minus waiting), and the waiting flag is cleared in the job's finally block. RuntimeInteractionPort relays enter/exit of _wait_until_ready through a wait_reporter callback, covering resume, termination and lease loss paths. The reporter degrades safely: a stale SDK copy without mark_waiting falls back to slot-consuming waits, and call_soon_threadsafe is wrapped in a lambda because it does not forward keyword arguments. Config: raise the env example defaults from 24h to 1h waits and concurrency 2 to 100, since waiting runs no longer consume execution slots. Tests: new test_waiting_jobs_do_not_consume_concurrency; scheduler suite 14/14, HITL service 12 passed, runtime and app suites 43/43. * test(scheduler): fix flaky waiting-concurrency assertion on fast event loops Root cause: job 2's executor completed instantly after appending to started, so its done-callback could discard it from _running before the active_count == 2 assertion ran. On Linux CI the event loop schedules that callback first, making the test fail intermittently. Fix: both executors now park on the shared gate via separate running events, so the assertion observes a stable running set instead of a transient window. * Fix: drop unrelated AIDP interface refactor from the AIDP knowledge base fix (#3980) The AIDP knowledge base fix reached hotfix/v2.6.1 through PR #3967, which also carried two unrelated upstream changes that this release branch never had: - #3909 Knowledge base interface optimization (AIDP UI refactor) - #3930 support AIDP knowledge file deletion and download Both are removed here so the release line keeps only the bug fix. - Restore the AIDP frontend components to their pre-refactor layout and drop the helper modules only the refactor used: AidpKnowledgeBaseModalParts, useAidpGroupOptions, aidpUploadUtils. - Drop the #3930 document remove/download endpoints from services/api.ts and the AIDP translations that only those screens referenced. - Keep the fix itself unchanged: knowledge-base scoped Channels and KnowledgeFiles/History paths, all-status document listing, keyword search, status labels (UPLOADING / PROCESSING / EXTRACTING) and the upload-triggered polling. Verified: - pytest test/ext_components/aidp -q -> 583 passed - frontend `npm run type-check` (tsc --noEmit) -> no errors * fix(agent): accept reasoning-prefixed code actions (#3990) * refactor: remove human interaction features and related configurations (#3988) * refactor: remove human interaction features and related configurations * refactor: remove human interaction features and related configurations --------- Co-authored-by: cj2026-bit <647646783@qq.com> Co-authored-by: lijiayang619 <1170349871@qq.com> Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> Co-authored-by: bernard1234 <840646206@qq.com> Co-authored-by: panyehong <91180085+YehongPan@users.noreply.github.com> Co-authored-by: Jason Wang <56037774+JasonW404@users.noreply.github.com> Co-authored-by: gs-aion <gs597153711@qq.com> Co-authored-by: Dallas98 <40557804+Dallas98@users.noreply.github.com> Co-authored-by: chase <byzhangxin11@126.com> | 8 小时前 | |
fix(memory): match AIDP plugin entry filename case (#3939) Rename the bundled AIDP provider entry file to match plugin.yaml exactly and add a case-sensitive manifest regression test. Generated-by: gpt-5 Co-authored-by: Codex <noreply@openai.com> | 6 天前 | |
feat: knowledgebase capacity quota and warning (#3451) * feat(quota): add storage quota enforcement * feat(quota): add quota management interface and warnings * :bug: developer warning depressed * feat(quota): enforce platform tenant allocation * fix: preserve quota patch field semantics * test: include quota coverage in CI suite * test: cover quota platform edge cases * fix: localize quota allocation conflicts * feat: refine platform quota overview layout * fix: restore platform quota panel scrolling | 2 个月前 | |
✨ feature personal kb capacity (#3712) * feat(resource): personal knowledge base capacity management and quota APIs * test(quota): send default bearer header in client fixture * feat: add personal knowledge base capacity controls * fix: avoid logging personal KB request data * fix: avoid logging quota request identity * test: align model app tests with legacy permissions * fix: preserve explicit roles in speed mode * test: make dreaming age test time-stable * fix: clean up personal knowledge base permissions * test: improve personal knowledge base coverage * fix: localize personal knowledge base quota errors * fix: allow deletion of legacy KB source files * fix: complete personal knowledge base permission updates * fix frontend API error details typing | 1 个月前 | |
fix(agent): accept reasoning-prefixed code actions (#3989) | 1 天前 | |
fix(model): remove SSRF guard from OpenAI-compatible provider discovery (#4004) The guard rejected private/reserved IP literals and DNS names resolving into non-routable ranges before fetching {base_url}/models. Internal and IP-based deployments (including private-network OpenAI-compatible endpoints) were blocked from batch model discovery. Remove the _reject_non_public_ip / _validate_provider_base_url / _assert_resolved_ips_public chain and the related tests; keep the ssl_verify TLS toggle, which is independent of the guard. Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> | 5 小时前 | |
♻️ Refactor: Optimization of the Agent Creation Experience (#3926) | 8 天前 | |
fix: harden quota, tag, model and API-key contracts found by daily test suite (#4002) - quota: reject negative GB/MB inputs and warning>=critical threshold pairs (previously persisted as 200 with semantically inverted config) - api-key: mask access_key in /api-keys and /user/tokens list responses; only create/refresh may return the full secret once - tag: translate the DB trigger's 'Tag assignment limit exceeded' (without the 'Resource ' prefix) into a structured 409, and flush replacement deletes before inserts so a full tag replacement no longer trips the assignment-capacity trigger - model: propagate ValueError from create/batch_create so duplicate display names map to 409 and malformed batch entries to 422 instead of 500; include model_type in /model/llm_list items; tolerate quick-config entries without model_repo in get_model_name_from_config - test: align model service tests with the ValueError passthrough contract Co-authored-by: chase <byzhangxin11@126.com> | 5 小时前 | |
🐛 rm .env.bak | 1 年前 | |
♻️ all code comments in english and code clean (utils) | 1 年前 | |
✨Feature: Logs are written to disk as files (#3859) * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut | 18 天前 | |
✨Feature: Logs are written to disk as files (#3859) * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut | 18 天前 | |
✨ Support full thread management (#3917) * 📃 Merge cursor rules into universal AGENTS.md and skills * ✨ Support full thread management * ✨ Support full thread management phase2 * 🧪 Add test files * 🧪 Add test files * 🧪 Add test files * ✨ Thread management fulfilling platform constraint * 🧪 Add test files * 🧪 Add thread interface * 🧪 Add test files | 8 天前 | |
✨Feature: Logs are written to disk as files (#3859) * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut | 18 天前 | |
merge(develop): merge v2.6.1 hotfixes from hotfix/v2.6.1 (#3998) * 🐛 Fix(evaluation): run trials in runtime service (#3954) * fix(evaluation): run trials in runtime service Route trial evaluations through the authenticated Config-to-Runtime proxy and use Config's manager only for creation-stage preparation. Keep Agent execution and evaluator scoring in Runtime. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub config thread manager Keep pure-logic service import tests aligned with the Config and Runtime thread-manager split. Co-authored-by: Codex <noreply@openai.com> Generated-by: gpt-5 * test(evaluation): stub runtime jwt helper * test(evaluation): cover trial proxy error paths * Fix/override delete (#3958) * Fix: override dialog only shows override values, not model defaults (deleted params no longer reappear) * Fix: custom param deletion persists (null markers), per-agent capacity overrides take effect, and edit-dialog connectivity probe uses stored api_key * Fix: rename ModelRequest.model_id to probe_model_id - model_dump() is spread into INSERT column lists, so a model_id field injected an explicit NULL primary key and broke model creation * Fix: move probe_model_id to a dedicated ModelProbeRequest subclass - ModelRequest.model_dump() is spread into INSERT column lists, so any non-column field breaks model creation (Unconsumed column names) * Fix: editing/adding a model no longer steals the occupied default-model slot - persistCustomLocalConfig now only writes the slot when it is empty (onboarding) or the submitted model already occupies it * Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots) * Revert "Fix: remove persistCustomLocalConfig - the frontend-cached-config guard could still steal an occupied default slot when the cache was stale/empty. Default-slot writes now come only from the server (create-time backfill for empty/dangling slots)" This reverts commit 2376aa50ccb0f170e5412a14c5aee33798ca1f06. * Fix: VLM connectivity probe never found the local test image - the gateway adapter's relative dirname chain resolved two levels short of the package root, so every probe silently fell back to a public URL that is unreachable in offline deployments. Anchor both probe copies on nexent.__file__ so the path survives module moves. --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * cherry-pick: HITL bugfixes from PR #3948 into hotfix/v2.6.1 (#3959) * Bubfix: guarantee event order, harden chunk buffer, SSE-subscribe controller, and break adapter on terminal human_run (#3948) * fix(hitl): preserve correct event ordering between observer chunks and human_interaction requests Root cause: the worker thread writes human_interaction events synchronously via SQLAlchemy in ask_user, while model_output_thinking/parse observer messages flow through the async consumer and are flushed only on a batched threshold (32 chunks or 250ms). When the worker suspends before that flush fires, human_interaction gains a lower event_seq number than the already-buffered observer chunks, causing the SSE replay stream to show them in the wrong order. Fix: replace the plain async-for consumer loop with a manual asyncio.wait iterator using a 50ms timeout. Once the worker finishes producing model output (i.e. right before ask_user), the loop times out and flushes any buffered observer chunks to the DB first, guaranteeing they precede the subsequent human_interaction row. Empty queue idle periods are essentially zero-cost; overall DB write frequency stays on par with the original. * fix(hitl): guarantee observer chunks precede human_interaction in DB event order When the agent invokes ask_user, two independent write paths caused the human_interaction row to be persisted BEFORE model_output_thinking / parse chunks, breaking the SSE replay ordering: the worker thread writes HITL events synchronously via SQLAlchemy, while observer messages flow through the async consumer which only flushes on a batched threshold. Fix: introduce a thread-safe shared chunk buffer on RuntimeInteractionPort (port.add_chunk / port.take_chunks). The async consumer pushes every processed chunk there; the worker thread calls flush_chunks_until_idle() before dispatching any HITL event — it polls the shared buffer and waits for the async loop to drain the observer queue (20ms idle window, 500ms max wait), then persists every chunk in its own transaction. This guarantees chunk event_seq < human_interaction event_seq regardless of async scheduling latency. Also fix ImportError: openai 2.50 removed the httpx2 module. OpenAIModel now falls back from httpx2 to httpx at import time. * test(hitl): cover shared chunk buffer, flush_chunks_until_idle, and httpx2-fallback paths Add unit tests for the RuntimeInteractionPort thread-safe chunk buffer and the flush_chunks_until_idle poll loop that guarantees observer chunks precede human_interaction events in DB order. All five HITL entry points (dispatch / boundary / receipt / finish / _wait_until_ready) are verified to invoke the idle flush before opening their transaction. Also add two tests for the openai_llm httpx2 → httpx ImportError fallback introduced to support openai >= 2.50 where the httpx2 shim was removed: one covers the fallback path, one confirms httpx2 still wins when present. * fix(hitl): address 4 review comments — hard deadline, emit_in_flight, peek_chunks, try/except safety Fix 4 real issues flagged by github-code-review: 1. Non-resettable hard_deadline in flush_chunks_until_idle — previously reset on every drain, meaning a model that kept producing chunks could stall the worker forever. deadline is now computed once at entry and the sleep call clips to hard_deadline - now. 2. _emit_in_flight Event bridges the async emit path and the worker's idle poll. Without this, buffer-empty = 'persisted' was confused with buffer-empty = 'taken for emit but still in run_blocking queue'. The worker now checks both 'buffer empty for settle_ms' AND 'no emit in flight' before deciding the async side is truly idle. 3. peek_chunks() replaces the take-put-back pattern in _flush_if_due. Previously the async loop drained the buffer, decided it was not yet due, then put everything back. That transiently-empty window (16 us normally, arbitrarily long under GIL/GC/preemption) was enough for the worker's 20 ms poll to mis-fire. We now peek (read count, no drain) and only take_chunks when we actually intend to persist. 4. emit_chunks wrapped in try/except that puts drained chunks back into the shared buffer before re-raising, and finish() wraps its flush call in try/except: pass. Guarantees (a) no chunk loss on DB failure and (b) the terminal human_run row is always written even if the flush step fails. Tests added: - 12 pure-mock unit tests in test_runtime_port_chunk_buffer.py cover hard_deadline, _emit_in_flight, peek_chunks, begin_emit/end_emit, try/except path, and every HITL entry-point's flush-before-transaction. - 1 async execute_attempt integration test in new test_application_execute_attempt.py drives the full consumer loop through _flush_if_due (peek → take → begin/end_emit) and the final flush, verifying that every patch line added in application.py is hit. * perf(hitl): stop polling while SSE stream is active, fallback to 5s when disconnected When isRunning=true the EventSource already pushes human_interaction and human_execution events in real time — the 1.5s polling loop duplicated that work, hitting the DB and re-rendering the frontend for every tick. Disable polling entirely while the SSE stream is alive, and drop to 5s intervals only when the stream is closed (e.g. page load before the first run, or after a run finishes) so we can still discover WAITING_HUMAN requests that were created while the client was disconnected. Add isRunning to the useEffect dependency array so the polling cadence resets immediately when the SSE connection state changes. * perf(hitl): replace polling with SSE subscription and move snapshot off the write path Frontend — /conversation polling → /{run_id}/events SSE: - Replace the 5s conversation snapshot polling with a native EventSource subscription to the backend's /{run_id}/events SSE stream. Discovery is now one-shot: conversationId change and the agent stream pause (isRunning true→false), the exact moment a HITL run is most likely to exist. The SSE stream then keeps run state live with native auto-reconnect. - Add dual guards inside refresh() to absorb the thundering herd from adapter.onHumanInteractionEvent (fires once per HITL SSE chunk) plus our own SSE effect: (1) in-flight dedupe — one snapshot absorbs all concurrent callers and returns cached state; (2) 3s minimum interval so bursts after the in-flight resolves do not immediately re-hit DB. - Use a runRef mirror so refresh() stays stable and downstream effects do not re-run on every snapshot. - Detect terminal status inside SSE onmessage and proactively es.close() to prevent EventSource from reconnecting forever against COMPLETED runs. Backend — snapshot off the write path: - Add repository.read_only() context manager: plain SELECT without WITH FOR UPDATE, no transaction, no flush, no _expire scan. Pure reads must not contend with worker writes on the same row lock. - Add service.light_snapshot() using read_only. Retain snapshot() as a writer-path API for any future lock-held callers. - Route conversation_snapshot, run snapshot endpoint, and both snapshot calls inside stream_run() through light_snapshot. - Move expiration to the writer path: decide() still calls _expire inline before processing each request, and expire_waiting() remains the periodic scheduler sweep. Impact: conversation snapshot calls drop from 12+/min (polling) or 10+/s (burst from adapter + SSE) to at most one every 3s. Each call is now two plain SELECTs instead of a lock-held transaction with a possible write from _expire. Read and write paths are fully decoupled. * fix(hitl): detect terminal human_run in adapter and break stream so isRunning flips false After a HITL run reaches FAILED/COMPLETED, Assistant-UI's isRunning stayed true — the stop button remained visible and new messages went into the queue buffer instead of being sent normally. The root cause is that isRunning is driven entirely by the ChatModelRun generator lifetime, which only returns when the backend SSE HTTP connection closes (reader.read() -> done=true). The backend stream_run loop can hang on heartbeat even after the run is terminal when the SSE was opened during WAITING_HUMAN with attempt_active=true: the break condition requires both cursor >= event_seq AND (terminal status OR WAITING_HUMAN with attempt_active=false and empty rows). If continueHitl fires mid-flight with a stale after_event, the cursor never catches up, so the SSE stays alive forever and the generator never returns. Stop depending on the backend closing first. Inside the adapter's SSE chunk loop, detect a terminal human_run event (status in COMPLETED, FAILED, STOPPED, EXPIRED), set a hitlTerminal flag, break the inner for-loop, and let the outer while-loop exit via the same flag on the next iteration. Assistant-UI sees the generator return and flips isRunning false immediately. Only affects HITL streams — the normal non-HITL agent path never emits human_run events so this branch is never taken. * test(hitl): update mock from snapshot to light_snapshot after read-path refactor test_human_interaction_app.py still mocked service.snapshot after commit 288ae4e69 moved conversation_snapshot and the run snapshot endpoint to service.light_snapshot (read-only path, no lock, no _expire). The fixture return_value and the two assert_called_once_with/assert_not_called assertions all referenced the old method name, causing CI to fail because MagicMock.snapshot was never called. * test(hitl): raise diff coverage above the 90% merge gate Codecov reported 70.43% patch coverage (target 90%) because new error and race paths in the HITL changes had no tests. Add mocked unit tests for: leftover chunk flush in execute_attempt's finally block before the failed finish, CancelledError scope and stop-event fallbacks, RunTerminated finish race, recovery-required outcome, and chunk iterator aclose failure tolerance; runtime_port in-flight emit busy detection, chunk restoration when emit_chunks raises, and terminal status persistence on flush failure; light_snapshot/read_only service behavior with signed tenant and user scoping; and the httpx fallback when openai._base_client.httpx2 is absent. Measured locally with CI-equivalent per-file pytest isolation: patch coverage 202/202 = 100%. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * style(hitl): unify comment style across HITL changes Merge explanatory inline comments into docstrings, keep single-line comments for inline notes, convert TypeScript block notes to JSDoc, and drop banner/separator lines. Comment-level changes only, no behavior change. * refactor(hitl-test): dedupe fake port setup to satisfy SonarCloud duplication gate SonarCloud failed the quality gate with new_duplicated_lines_density=5.2% (threshold 3%), caused solely by test_application_execute_attempt.py: the inline _Port stub in the flush test and the one in _run_execute_attempt duplicated ~69 lines (2 CPD blocks, 14.4% file density). Extract a shared _build_port_class/_make_port_factory plus a _patched_application context manager and _execute_attempt_args so both call sites reuse a single definition; drop dead code (last_flush, install/monkeypatches, unused imports) and fix the latent bare-contextmanager NameError by using contextlib.contextmanager. No behavioral change; all 8 tests pass. * fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm openai >= 2.50 removed the httpx2 shim from openai._base_client. The bare import httpx2 causes ImportError in CI and on systems with recent openai versions. This restores the try/except fallback introduced in PR #3948 commit 9521b934 and later accidentally reverted by commit d086da259. * Revert "fix(sdk): restore httpx2 → httpx ImportError fallback in openai_llm" This reverts commit 8e06ce348dfc8c34baf42c6bfa8211883d0a2c61. * 🐛 Bugfix: Fixed an issue where the sandbox container user lacked the permissions to create folders and files. (#3963) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngi… (#3962) * Fix: dispatch ModelEngine provider listing to the dedicated ModelEngineProvider - get_provider_models routed every provider through the OpenAI-compatible adapter, so ModelEngine batch import failed (wrong endpoint path /open/router/v1/models, self-signed cert, custom type taxonomy, missing per-model base_url). The dedicated class existed but was never wired in. * chore: ModelEngine catalog base_url placeholder - preset public URL is wrong for private deployments, placeholder communicates the required /open/router/v1 path format --------- Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> * [codex] fix(agent): silently retry transient model errors (#3965) * fix(agent): retry transient model failures silently * fix(model): support OpenAI httpx2 timeout client * test(model): add deterministic OpenAI-compatible mock * fix(agent): keep stream runtime within line budget * Fix: AIDP knowledge base bug fix (#3967) * Fix: editing a ModelEngine model no longer flips ssl_verify to True - the update path only checked api_key emptiness while the create path also exempts open/router URLs (ModelEngine self-signed certs). The edit dialog prefills the real key and always submits it, so any edit silently broke connectivity. Exemption now checks the payload URL with a fallback to the stored record; batch-edit groups get the same protection * refactor: extract MODEL_ENGINE_URL_MARKER constant (SonarCloud S1192) and use a placeholder domain in test fixtures - no behavior change * [fix] enforce explicit CodeAgent termination and silent recovery (#3969) * fix(agent): enforce explicit CodeAgent termination * fix(test): restore CodeAgent CI compatibility * cherry-pick: HITL reliability fixes from PR #3977 into hotfix/v2.6.1 (#3981) * Fix StopAsyncIteration leak in execute_attempt finally block Root cause: when the agent chunk stream exhausted normally, the finally block awaited the already-consumed anext_task, re-raising StopAsyncIteration which was not suppressed by the existing CancelledError handling. The leftover chunk flush was skipped, successful runs were marked as failed, and the claiming scheduler job logged errors. Fix: reset anext_task to None before breaking out of the consumption loop so the finally block skips the await and always reaches the leftover flush and terminal finish() write. Tightened the regression test to assert that a normally exhausted stream does not leak StopAsyncIteration and that finish() is called. Also deduplicated the two _Port stub classes via a shared factory to satisfy the SonarCloud new_duplicated_lines_density gate. * Fix HITL form not appearing until page refresh Root cause: the frontend discovery chain rate-limited every refresh() with a 3s min interval and in-flight coalescing, silently dropping the critical human_run/human_interaction events that follow an ask_user suspension. The run event stream goes quiet afterwards, so nothing re-triggered the snapshot and the form only appeared after a manual page reload. Fix: refresh() now takes a force flag that bypasses the throttle; force callers arriving while a snapshot is in flight are re-run via a trailing refreshRef invocation instead of being dropped. SSE human_run/human_interaction/human_decision/human_execution messages and the chat-adapter onHumanInteractionEvent callback now force refresh. * Fix SSE chunk/HITL event ordering race under real server load Root cause: chunk persistence and HITL event writes ran in two threads (async consumer via run_blocking on the control-io lane, worker thread synchronously) with seq assigned at DB row-lock acquisition time. Two race windows reordered messages on loaded servers but never locally: (1) flush_chunks_until_idle's 500ms hard deadline fired while the async drain was still in flight, so the HITL row committed before chunks produced earlier (form appearing before model output); (2) worker emit_chunks and the async _flush_if_due drained concurrently without mutual exclusion, so seq order followed lock acquisition instead of production order. Fix: replace the begin_emit/end_emit Event with a shared threading.Lock and move take_chunks+emit_chunks into one atomic critical section (drain_and_emit) used by both the async consumer and the worker flush. The hard deadline may now only fire once the lock is free, guaranteeing in-flight drains commit before the caller writes its HITL transaction. Added regressions: flush waiting for an in-flight drain past its deadline, and concurrent drains preserving chunk production order. * fix(hitl): recover pending form when SSE delivery stalls silently Root cause: form discovery relied solely on a single EventSource plus refresh() with no fallback. A half-open connection (e.g. hung dev proxy) never raises an error event or reconnects, so human_interaction events are lost until a manual page refresh. A hung snapshot fetch could also keep refreshInFlight stuck forever, silently dropping every later refresh, including forced ones. Changes: - Poll the read-only snapshot every 5s while a run is active; the tick shares the refresh throttle and in-flight guard, so it adds no load while SSE delivery is healthy and discovers a pending form within 5s when the stream stalls - Add a 15s AbortSignal timeout to human-interaction client requests so a hung fetch releases the in-flight guard instead of bricking it - Wrap the human_run chunk JSON.parse in the chat adapter with try/catch: the stream loop has a finally but no catch, so a malformed payload would silently kill the whole chat stream read loop * fix(hitl): stop parked human-input waits from consuming scheduler slots Root cause: while a run waits for a human decision, its executor task parks inside _wait_until_ready and the lease renewal loop keeps the lease alive, so the run occupies one HITL_MAX_CONCURRENCY slot for up to HITL_WAIT_SECONDS (default 24h). With HITL enabled every non-debug chat is dispatched through this scheduler, so two unattended forms filled the default concurrency of 2 and froze all conversations: new agent/run streams only emitted heartbeats because READY runs were never claimed. Fix: add LeaseScheduler.mark_waiting so executors can flag themselves as parked on external input. Slot capacity is now max_concurrency minus executing jobs only (running minus waiting), and the waiting flag is cleared in the job's finally block. RuntimeInteractionPort relays enter/exit of _wait_until_ready through a wait_reporter callback, covering resume, termination and lease loss paths. The reporter degrades safely: a stale SDK copy without mark_waiting falls back to slot-consuming waits, and call_soon_threadsafe is wrapped in a lambda because it does not forward keyword arguments. Config: raise the env example defaults from 24h to 1h waits and concurrency 2 to 100, since waiting runs no longer consume execution slots. Tests: new test_waiting_jobs_do_not_consume_concurrency; scheduler suite 14/14, HITL service 12 passed, runtime and app suites 43/43. * test(scheduler): fix flaky waiting-concurrency assertion on fast event loops Root cause: job 2's executor completed instantly after appending to started, so its done-callback could discard it from _running before the active_count == 2 assertion ran. On Linux CI the event loop schedules that callback first, making the test fail intermittently. Fix: both executors now park on the shared gate via separate running events, so the assertion observes a stable running set instead of a transient window. * Fix: drop unrelated AIDP interface refactor from the AIDP knowledge base fix (#3980) The AIDP knowledge base fix reached hotfix/v2.6.1 through PR #3967, which also carried two unrelated upstream changes that this release branch never had: - #3909 Knowledge base interface optimization (AIDP UI refactor) - #3930 support AIDP knowledge file deletion and download Both are removed here so the release line keeps only the bug fix. - Restore the AIDP frontend components to their pre-refactor layout and drop the helper modules only the refactor used: AidpKnowledgeBaseModalParts, useAidpGroupOptions, aidpUploadUtils. - Drop the #3930 document remove/download endpoints from services/api.ts and the AIDP translations that only those screens referenced. - Keep the fix itself unchanged: knowledge-base scoped Channels and KnowledgeFiles/History paths, all-status document listing, keyword search, status labels (UPLOADING / PROCESSING / EXTRACTING) and the upload-triggered polling. Verified: - pytest test/ext_components/aidp -q -> 583 passed - frontend `npm run type-check` (tsc --noEmit) -> no errors * fix(agent): accept reasoning-prefixed code actions (#3990) * refactor: remove human interaction features and related configurations (#3988) * refactor: remove human interaction features and related configurations * refactor: remove human interaction features and related configurations --------- Co-authored-by: cj2026-bit <647646783@qq.com> Co-authored-by: lijiayang619 <1170349871@qq.com> Co-authored-by: ljy <ljy@DESKTOP-65OBISN.(none)> Co-authored-by: bernard1234 <840646206@qq.com> Co-authored-by: panyehong <91180085+YehongPan@users.noreply.github.com> Co-authored-by: Jason Wang <56037774+JasonW404@users.noreply.github.com> Co-authored-by: gs-aion <gs597153711@qq.com> Co-authored-by: Dallas98 <40557804+Dallas98@users.noreply.github.com> Co-authored-by: chase <byzhangxin11@126.com> | 8 小时前 | |
✨Feature: Logs are written to disk as files (#3859) * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut * ✨Feature: Logs are written to disk as files: ut | 18 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 8 天前 | ||
| 1 天前 | ||
| 5 小时前 | ||
| 4 个月前 | ||
| 8 小时前 | ||
| 8 小时前 | ||
| 15 天前 | ||
| 5 小时前 | ||
| 8 小时前 | ||
| 8 小时前 | ||
| 6 天前 | ||
| 2 个月前 | ||
| 1 个月前 | ||
| 1 天前 | ||
| 5 小时前 | ||
| 8 天前 | ||
| 5 小时前 | ||
| 1 年前 | ||
| 1 年前 | ||
| 18 天前 | ||
| 18 天前 | ||
| 8 天前 | ||
| 18 天前 | ||
| 8 小时前 | ||
| 18 天前 |