Commit Graph

497 Commits

Author SHA1 Message Date
NCBM 3c555278d5 docs: describe the disabled-thinking fallback 2026-10-16 22:16:35 +08:00
NCBM f39ec5fb1a core/agent+anthropic: drop the thinking block a model refuses to disable 2026-10-16 22:16:25 +08:00
NCBM 06a5bafab7 docs: describe the adaptive-thinking retry 2026-10-16 22:12:09 +08:00
NCBM 9156df2b02 core/agent+anthropic: retry a refused thinking budget as adaptive thinking 2026-10-16 22:12:00 +08:00
NCBM 3e7acbaf18 release: plyngent 0.9.0 v0.9.0 2026-10-16 19:46:54 +08:00
NCBM c55c5be779 deps: refresh the lock (sqlalchemy 2.1.4, platformdirs 4.13.3, tomlkit 0.15.2, pywinpty 3.1.0) 2026-10-16 19:46:44 +08:00
NCBM b0afed612f docs: describe the same-file write ordering of a batch 2026-10-16 19:37:38 +08:00
NCBM 85b0318b77 core/agent+tools: apply a batch's same-file writes in call order 2026-10-16 19:37:29 +08:00
NCBM ab6b5d590c docs: describe the escape error's access mode 2026-10-16 19:29:20 +08:00
NCBM 1c84d63f37 core/tools+config: name the access mode in the workspace escape error 2026-10-16 19:29:11 +08:00
NCBM a4f852eec6 docs: describe the batch command listing 2026-10-16 19:08:24 +08:00
NCBM 6232925c2c cli/display: list a batch's commands above the '* Run batch' row 2026-10-16 19:08:14 +08:00
NCBM 60dd96329d docs: describe the numbered todo listing 2026-10-16 18:53:10 +08:00
NCBM 3bc585ad3d docs: describe the write_file overwrite rule 2026-10-16 18:52:59 +08:00
NCBM 2eb99a079d core/agent+tools+cli: number todos by listing order, not the internal id 2026-10-16 18:52:36 +08:00
NCBM ac33e92d33 core/tools+config: refuse write_file overwrite unless overwrite=true 2026-10-16 18:44:14 +08:00
NCBM 8729330120 core/skills: announce the skill tools in a notice, not a catalog 2026-10-09 21:40:45 +08:00
NCBM e72d2359d7 core/skills: read the filesystem live, drop the cache and reload 2026-10-09 21:40:38 +08:00
NCBM 1f04889449 cli/skills: open the skill directory instead of printing SKILL.md 2026-10-09 21:40:27 +08:00
NCBM 64a8cf7062 docs: drop the /clear references 2026-10-09 21:15:55 +08:00
NCBM 6807055b9f cli/slash: remove the /clear command 2026-10-09 21:15:48 +08:00
NCBM 287b838db9 docs: drop the multi-tenant router plan 2026-10-09 21:15:07 +08:00
NCBM d3f8132b5d core/agent: note the live-probe result for the echoed blocks
A search round goes back with and without a local `tool_use` after its blocks: the endpoint takes both, and the model answers from the results it already holds. With the blocks stripped it calls its own earlier answer fabricated instead. The note sits where the ordering decision lives, so the shape needs no re-probe.
2026-10-09 20:15:38 +08:00
NCBM b0116a67c1 docs: describe the kept provider blocks 2026-10-09 20:09:43 +08:00
NCBM f89c8dec31 lmproto/openai_compatible: drop provider blocks on a chat wire
`provider_blocks` means something only to the surface that produced them (Anthropic `thinking` / server-tool blocks). A session that switched surface would otherwise send those dicts to a chat-completions provider as an assistant-message key it does not know; the request normalization drops the field, on a copy, so the history keeps it.
2026-10-09 20:09:36 +08:00
NCBM 7a54642ecf core/agent: rebuild a streamed round's provider blocks
Streaming is the default path: `thinking` fragments, a `server_tool_use`'s `partial_json` query and a `web_search_tool_result`'s entries arrive as separate SSE events, so the blocks the round has to hand back exist only as fragments. The dispatch rebuilds each one from its start payload (field for field, under the stream's own names) plus the fragments that follow, and passes it to the loop as a chunk; the loop copies it onto the round's message, where a round that kept only those blocks counts as payload rather than an empty completion.

The thinking text streams as reasoning as it arrives — the search query and the signature go back with the round only.
2026-10-09 20:09:29 +08:00
NCBM 2fdbc3ce5d core/agent: keep a Messages round's provider blocks on the assistant message
DeepSeek's Anthropic compat answers a hosted search inside the turn: the response carries the `thinking` that picked the query, the `server_tool_use`, and the `web_search_tool_result`. The response mapping kept text and tool_use only, so the round lost its evidence — and the `thinking` signature, which only the provider can verify, could not go back at all. `AssistantChatMessage.provider_blocks` carries those blocks as raw JSON now (`thinking` text *and* signature, the query, the result entries with an `encrypted_content` the API rejects when trimmed) and the assistant turn hands them back first, ahead of its text and tool_use blocks; `thinking` also lands in `reasoning_content`, so the chain of thought streams as reasoning like every other surface's.

`AnthropicRawContentBlock` names the provider fields a streamed block carries, and `AnthropicServerToolUseContent` keeps the `caller` metadata the wire sends.
2026-10-09 20:09:20 +08:00
NCBM 6c4c8b9328 core/tools: edit_lineno rejections name the read_file call 2026-10-09 19:46:16 +08:00
NCBM 279cbe9389 core/tools: document fetch's 0-based body offset 2026-10-09 19:46:09 +08:00
NCBM 38e2dd58b0 core/config: state the 0-based offsets of skill_read and fetch 2026-10-09 19:46:02 +08:00
NCBM a89543fd35 release: plyngent 0.8.0 v0.8.0 2026-10-07 00:51:49 +08:00
NCBM 9296242881 ci/lint: ignore PLR0917 like PLR0913 2026-10-07 00:47:56 +08:00
NCBM 5a7bd55f2d lmproto: keep None last in the optional unions 2026-10-07 00:47:20 +08:00
NCBM c56449bfce deps/dev: upgrade ruff to 0.16.10 and basedpyright to 1.40.2 2026-10-07 00:47:03 +08:00
NCBM 9fc74e1866 deps: refresh the lock (msgspec 0.22, sqlalchemy 2.1, niquests 3.21.2) 2026-10-07 00:43:54 +08:00
NCBM 6e8368c169 docs: record the complexity gate and the nested-function counting 2026-10-07 00:40:31 +08:00
NCBM e0e19fde8f ci/lint: drop PLR0912 as a duplicate of C901 2026-10-07 00:40:08 +08:00
NCBM 9fac1ecfc0 core/agent+cli: give each C901 exemption a reason 2026-10-07 00:39:30 +08:00
NCBM 432b703b54 docs: describe what a repeated reasoning text does
The ``responses_bridge`` bullet records that ``response.reasoning_text.done`` repeats the fragments: the loop streams each part once, and the round's message still carries the whole chain of thought back to the API.
2026-10-07 00:26:53 +08:00
NCBM bf8203ae37 core/agent: stream a repeated reasoning text only once
DeepSeek's Responses stream sends ``response.reasoning_text.delta`` fragments and then ``response.reasoning_text.done`` carrying the whole chain of thought those fragments spelled out. The loop folded a full delta into the round — it replaces the fragments, and that is what the assistant message hands back to the API — but streamed every delta verbatim, so the chain of thought was printed twice: once as it arrived and again as the ``.done`` text.

A full delta now streams only a tail the fragments did not cover, and nothing at all when it merely repeats them; a round whose fragments never arrived still streams the full text once. The message keeps the complete text either way.
2026-10-07 00:26:38 +08:00
NCBM 4b5f67f5f3 cli/display: render web_search as a pretty line
``web_search`` is composed by the CLI host rather than the builtin catalog, so it had no ``_PRETTY_LINES`` entry and fell back to the ``[tool] …`` / ``[tool ok] …`` whole lines. It now leads with ``* Web Search '<query>' `` (the query as a 60-character preview, like the ask and skill subjects) and lands on the hit count — or on ``(no results)`` when every source came up empty, an emptiness the tool reports as ``error: the search found nothing`` and which is therefore not shown as a failure. A real error still prints in red with its message.

The guard test that keeps a builtin tool from shipping without a renderer now checks this host-composed tool too, since a catalog walk cannot see it.
2026-10-07 00:20:41 +08:00
NCBM 933d5385b9 docs: describe the search sources
The ``web_search`` bullet lists both sources, the fallback order and the
snippet, and says the tool needs a non-empty ``search_sources``.
2026-10-07 00:09:52 +08:00
NCBM 2bb6e06171 core/tools: search a list of sources, DeepSeek's index then Bing
``search_sources`` (a provider list, ``["deepseek", "bing"]`` by default,
``[]`` to not offer the tool at all) names what the local ``web_search`` tool
searches with, in fallback order: ``search_in_order`` asks each source in turn
and keeps the first answer, so a source that errors — a rate limit, a rejected
key — or finds nothing hands over to the next instead of failing the tool. The
handler reports the failures only when every source came up empty.

The Bing leg answers with its RSS results (``?format=rss``), which need no key
and carry a description — the snippet DeepSeek's own index does not hand out —
and it goes through the ``fetch`` client, so the SSRF and host-grant policy
applies to it too (a host that binds tool instance state, as the CLI does).
2026-10-07 00:09:46 +08:00
NCBM 19ab86f590 docs: say which presets ask for provider_tools
The provider-tools bullet names DeepSeek next to OpenAI, since the default and
the effective-provider gate now cover both.
2026-10-06 23:59:07 +08:00
NCBM e877df9d94 config: default DeepSeek to web_search and honour its provider_tools
``provider_tools`` reached the effective provider only for the OpenAI preset,
so a DeepSeek ``provider_tools`` in TOML was dropped on the way — and the preset
defaulted to none, leaving search unavailable for every convention.

DeepSeek now defaults to ``[{type="web_search"}]`` like OpenAI does (``[]``
turns it off) and its list reaches the agent: the Anthropic convention runs the
hosted search inside the response, the Responses and chat conventions get the
local ``web_search`` tool (see ``config.routing.local_web_search``).
2026-10-06 23:59:01 +08:00
NCBM 27a901083a docs: describe the local search tool
The tools list gains the ``web_search`` entry (what it returns, when the CLI
registers it), and the provider-tools bullet says DeepSeek's preset asks for no
hosted tool by default.
2026-10-06 23:45:41 +08:00
NCBM a0d39bba6d core/tools: search through a local tool where a hosted one is ignored
Only DeepSeek's Anthropic-compatible surface runs a hosted ``web_search``: the
Responses and chat-completions surfaces answer "I can't browse the web" with
``{"type": "web_search"}`` and with the server-tool shape alike, so a DeepSeek
provider that talks them had no search at all.

``config.routing.local_web_search`` decides per provider: a DeepSeek provider
whose ``provider_tools`` ask for ``web_search`` and whose convention is not
``anthropic`` gets the local tool (``tools/net/search.py``), which asks the
search-capable endpoint behind the scenes and hands the model one ``title —
url`` line per hit — read a page with ``fetch``, the bodies stay server-side.
DeepSeek's preset asks for no hosted tool by default, so this stays opt-in.
2026-10-06 23:45:35 +08:00
NCBM e2ee937d8f lmproto: search through DeepSeek's Anthropic-compatible endpoint
DeepSeek runs ``web_search`` inside a turn — the request declares the server
tool, the response carries a ``server_tool_use`` per query and the
``web_search_tool_result`` it produced — so a search is one ``/messages`` call
whose hits are read out of the content blocks. The turn stops as soon as it has
searched (a live probe with 64 output tokens spent all of them before the search
and returned nothing, 512 answers with the hits).

Hits carry a title, a URL and an ``encrypted_content`` only the index can read,
so the hit list is all a caller gets: the page bodies stay server-side.
2026-10-06 23:45:29 +08:00
NCBM af232283a4 docs: describe a hosted search on the Anthropic surface
The ``messages_bridge`` bullet says a server tool is the surface's own business
(only ``tool_use`` becomes a local call), the provider-tools bullet says the
Messages surface declares its own definition, the ``anthropic/model.py`` bullet
lists the blocks that now decode, and the file map says as much.
2026-10-06 23:36:41 +08:00
NCBM 21f59ea33c core/agent: count the prompt a search turn really sent
The compat opens with the question alone (``message_start`` reports 165 tokens)
and puts what the search then pulled in on ``message_delta`` (12468); merging
preferred the earlier value, so the CLI's context gauge and the auto-compact
threshold ran ~12k short of the request. The larger of the two wins now (output
tokens are the delta's cumulative count either way).
2026-10-06 23:36:35 +08:00