fix: preserve tool argument keys in chat history by CHLIN0 · Pull Request #187 · lmstudio-ai/lmstudio-python · GitHub
Skip to content

fix: preserve tool argument keys in chat history - #187

Open
CHLIN0 wants to merge 1 commit into
lmstudio-ai:mainfrom
CHLIN0:fix/tool-argument-key-preservation
Open

CHLIN0 wants to merge 1 commit into
lmstudio-ai:mainfrom
CHLIN0:fix/tool-argument-key-preservation

Conversation

@CHLIN0

@CHLIN0 CHLIN0 commented Oct 21, 2026 •

Copy link
Copy Markdown

Tool call arguments imported with Chat.from_history(dict) or appended as an AssistantResponse must retain caller-defined keys. The previous normalizer changed nested keys and could collapse distinct order_id and orderId values.

Preserve the arguments boundary with JSON structural copying: mappings become fresh dictionaries without renaming keys, lists/tuples become fresh lists, and scalar values remain unchanged. Unlike the earlier PR revision's deepcopy approach, this normalizer materializes read-only mappings such as MappingProxyType accepted by ToolCallRequest.from_dict. ID-based cycle detection raises LMStudioValueError("Data structure cycles are not supported."), while nested lists remain allowed inside arguments. Enclosing SDK aliases and typed tool-request validation remain unchanged; generated models are untouched.

Coverage includes dictionary history and append, structured append, both collision orders, nested containers, bidirectional copy independence, Chat copies, legacy plugin payloads, and mapping validation. Ten additional cases in this revision cover top-level/nested read-only and custom mappings, mapping/proxy/list/tuple/envelope cycles, and nested sequences with shared references.

Validation on Python 3.12.9:

  • Focused schema/history: 70 passed, 1 deselected (37 added parameterized cases in the full patch, including 10 for the mapping regression, plus 33 existing cases). Only the unchanged async file-cache case was deselected.
  • Configured Ruff lint passed; full format check passed for 71 files.
  • Strict mypy passed for 67 source/test files and 17 example files (MYPYPATH=src).
  • The tested patch and committed diff match. This remains one amended commit above the original base.

No live integration, interpreter/platform matrix or distribution build was run. Selected Python I/O instrumentation is not OS/native behavior proof.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@github-actions github-actions Bot added the CLA signed Indicates that all contributors have signed label Oct 21, 2026
@CHLIN0

CHLIN0 commented Oct 21, 2026

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

1 similar comment
@BRlin-o

BRlin-o commented Oct 21, 2026

Copy link
Copy Markdown

I have read the CLA Document and I hereby sign the CLA

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/lmstudio/schemas.py Outdated
for k, v in input_dict.items():
if k == "arguments":
# Tool arguments are caller JSON, not SDK fields to normalize.
output_dict[k] = deepcopy(v)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep accepting read-only argument mappings

When ToolCallRequest.from_dict() is given a valid read-only Mapping such as types.MappingProxyType({"order_id": 1}), this line raises TypeError: cannot pickle 'mappingproxy' object before msgspec can validate it. arguments is explicitly typed as Mapping[str, ...], and the previous traversal accepted such implementations by materializing their items into a normal dictionary, so wrapper or immutable mappings now regress; copy mappings by traversing their JSON contents while leaving their keys untouched rather than calling deepcopy on the mapping object itself.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Fixed in 1e58c74: replaced deepcopy with a key-preserving structural copy — mappings (including read-only/custom ones like MappingProxyType) become fresh dicts, lists/tuples become fresh lists, and cycles raise LMStudioValueError as before. Added regressions for top-level/nested read-only and custom mappings, cycles, and shared nested values. Focused schema/history tests: 70 passed; Ruff, format and strict mypy pass.

@CHLIN0

CHLIN0 commented Oct 21, 2026

Copy link
Copy Markdown
Author

@CHLIN0
CHLIN0 force-pushed the fix/tool-argument-key-preservation branch from 2c9e402 to 1e58c74 Compare October 21, 2026 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA signed Indicates that all contributors have signed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants