Repository navigation
Conversation
`messages_from_input` starts a new assistant message for a `function_call` item unless it directly follows another call. One assistant turn is reported as several output items, its text first, so replaying the turn the way the endpoint produced it splits that turn into two consecutive assistant messages. Chat templates render every message as its own turn, so the replayed prompt gains an end-of-turn marker between the assistant text and its own tool call. `/v1/messages` and `/v1/chat/completions` keep such a turn in a single message carrying `tool_calls`, and the same conversation now renders identically on all three endpoints. Join the call to the preceding assistant message instead. That subsumes the adjacent-call grouping added in InternLM#5044, whose separate bookkeeping is no longer needed. A call that does not follow an assistant message still starts its own message.
c695272 to
39b4d9c
Compare
|
Updated the branch to current @RunningLeon @silencelamb could one of you approve the workflow run here and have a look? CI has never run on this PR, while #5058 got its runs authorized and came back green on all three, so a run is the only thing missing on this one. The behaviour is still on |
|
Updated the branch to current |
Motivation
Responses replies are returned with
store=False, so continuing a conversation means sending the previous output items back ininput. One assistant turn is reported as several output items — its text first, then its calls — and replaying that turn does not reconstruct it as one turn.messages_from_inputstarts a new assistant message for everyfunction_callitem unless it directly follows another call, so a turn that contained text and a tool call comes back as two consecutive assistant messages:/v1/messagesand/v1/chat/completionsrepresent that turn as a single assistant message carryingtool_calls.Chat templates render one turn per message, so the prompt the model receives is wrong in a different way for each template. Rendered through
MultimodalProcessor._get_text_prompt_input, same conversation, before this change:The tool call arrives after an end-of-turn marker, in a turn that was never opened; mistral gets a doubled
</s>; llama2 gains an empty user exchange. With this change all three templates produce exactly the prompt the equivalent/v1/messageshistory produces.Merged #5044 fixed the neighbouring case, where two adjacent
function_callitems became two turns instead of one parallel invocation. A call that follows the assistant's own text is still split off.Modification
function_callto the preceding assistant message whenever the previous message is one, instead of only when the previous item was itself a call. The assistant message is the turn being continued, so this covers the adjacent-call case too and#5044'sprevious_item_typebookkeeping is no longer needed; its comment about keeping every id visible to templates is kept.function_callthat does not follow an assistant message still starts its owncontent: Noneassistant message. That is the shapetest_responses_maps_function_call_history_to_chat_messagesasserts, and it is unchanged.main. The parallel-call case passes onmainsince fix(serve): preserve tool-result image ownership #5044 and is kept as a guard, so a future change to this branch cannot regroup those calls.Not touched:
/v1/messagesand/v1/chat/completionsalready produce the single-message shape.BC-breaking (Optional)
None at the protocol level. A replayed history that previously rendered as two assistant turns now renders as one, which is what the other two endpoints already did.
Use cases (Optional)
Before this change the prompt behind
secondcarries an end-of-turn marker between the assistant text and its tool call.Checklist
pytest tests/test_lmdeploy/serve— 666 passed against 664 onmain, plus the 2 new testsmainas well:test_tool_images.py::test_internvl_tool_image_placeholders_across_protocols[internvl3-*].tests/test_lmdeploy/conftest.pyreadsinternvl3.jinjawithPath.read_text()and noencoding, and that template contains non-ASCII text, so it raisesUnicodeDecodeErroron a Windows machine whose ANSI codepage is not UTF-8. Unrelated to this change and invisible on Linux CI.pre-commit run --all-files— all hooks passpylint lmdeploy/serve/openai/responses/request.py— same message set and exit code asmain; only line numbers moveinterrogate --fail-under 70 lmdeploy— passes at 71.7%