A queued send flushed by calling submitNow directly, which skipped the
loading check in canSubmit. submitNow clears the composer before the
consumer runs, and the consumer ignores a submit while status is
'loading'. If a retry or an edit was streaming when the attachments
settled, the question was cleared and never sent.
useArmedSend takes a canFlush flag. The composer passes whether it can
take a submit right now (not loading, not recording or transcribing).
The armed send stays queued until that holds, then goes out.
A failed attachment made getSendReadiness return 'blocked', so Send was a
no-op until the user found and removed the chip. On a phone the only hint
was a small red line after the tap, and a new user lost their question
that way. A failed file never recovers, so holding the send only costs
the question.
Readiness now ignores failed attachments: it waits only on uploading or
processing files. On submit the composer drops every failed attachment
and sends the text with the completed ones. The payloads already read
completed attachments only, so no failed id reaches the request or the
history. The hero-card path in Conversation also clears failed chips.
The failed chip still shows before Send and can be removed by hand.
Removes the send-blocked alert and its locale key.
The banner still announced 0.17, two releases back, through the old blog
domain. It now links the GitHub Secure Open Source Fund post on
www.docsgpt.cloud directly, skipping the blog.docsgpt.cloud redirect.
gen_continuation is not wrapped by @log_activity, so the llm_* log lines
from a resumed turn carried no user, agent, conversation or endpoint. Every
OpenAI-compatible /v1 client-tool round landed anonymous in the logs.
Add agent_log_context, which binds the same keys log_activity binds while
keeping any enclosing activity_id, and wrap gen_continuation in it. Both
now derive the keys from one helper.
A turn whose agent raised wrote no user_logs row, so it only surfaced as
the agent's system error row. Every finished turn now writes its chat row,
at level error with the error when it failed, and linked to its trace; the
system row for the same traced activity is no longer listed twice.
DeepSeek and every other OpenAI-compatible API run through OpenAILLM, so
spans and metrics reported them as openai. The provider now comes from the
endpoint: known API hosts map to their gen_ai.provider.name (deepseek,
azure.ai.openai, mistral_ai, ...), and an OpenAI client pointed anywhere
else is openai_compatible. LLM spans also record server.address.
Paused, denied, skipped and client-run tool calls all go through
trace_unexecuted_tool_call. The research synthesis, continuation and
workflow step spans use with-blocks instead of hand-rolled GeneratorExit
handling. Fixes blank lines and adds a missing hint and docstrings.
Only message-linked traces cascaded, so scheduled runs, stateless /v1
rounds and unreserved turns kept their previews after the conversation was
deleted; they are now deleted by conversation_id. The Logs search and graph
branches filter by source, so the listing indexes lead with it.
The OTel replay and the trace INSERT ran in the stream's finally, so a slow
database held the SSE connection open after the last event. The trace is
still frozen when the stream ends, but written on a small writer pool.
Span.fail stored the exception message in span.error, which is stored and
exported whatever the content settings; a provider's content-filter error
can quote the prompt. span.error now holds the exception type, and the
message is a capture-gated preview, like tool errors already were. A
yielded stream error on the agent span is treated the same way.
With TRACES_ENABLED off, LLM calls record no GenAI metrics, and a streamed
answer is joined into a preview only when a span keeps it. Agent runs and
continuations build their invoke_agent span in one place, which now reads
the model from model_id. The trace panel shows a stream's model time next
to how long it was open. Adds missing type hints.
A failed trace-summary lookup now leaves the Logs page intact without
chips. Tool-call counts include only calls that ran, not their paused,
denied or skipped records. A local guardrail that fires unchanged on every
streamed segment is recorded once, so it cannot use up the span cap.