mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-04 14:12:58 +00:00
The backend import package is now docsgpt, the name it will carry on PyPI; application was far too generic to install into anyone's site-packages. git mv plus a mechanical rewrite of every import, dotted string and path reference: 734 Python files, the compose files, Dockerfile, workflows, docs, setup scripts, devcontainer, k8s manifests, vscode config, pytest and coverage config, .gitignore. Behaviour is unchanged. Kept for one release: - A top-level application package whose meta-path finder resolves application.x.y to the already-imported docsgpt.x.y object, so old imports and entry points (celery -A application.app.celery, uvicorn application.asgi:asgi_app) keep working with a FutureWarning. - Celery registers every application.* task name as an alias of its docsgpt.* task on start-up, so messages queued by the previous release still run. The redbeat key prefix moves to redbeat:docsgpt:v2: so schedule entries the previous release wrote are left unread instead of firing twice. The backend image builds from the repository root (docker build -f docsgpt/Dockerfile .) so it can ship the alias package; a root .dockerignore allow-lists docsgpt/ and application/ and keeps caches, local data, .env files, the sample index files and the Dockerfile out. Compose and the image workflows point at the new context.
84 lines
3.2 KiB
Python
84 lines
3.2 KiB
Python
"""``get_truncated_tool_calls`` is what the client persists and reloads.
|
|
|
|
It is a hand-written key whitelist, and two keys the UI depends on were never
|
|
added to it. The per-call ``tool_call`` stream event carries the full dict, so
|
|
everything looks right live and then disappears on reload:
|
|
|
|
- ``artifacts`` — the per-file download chips. Without it a turn falls back to
|
|
the single ``artifact_id``, so a ``run_code`` that wrote three files shows one
|
|
chip labelled "Code Executor" instead of three labelled by filename. That is
|
|
precisely the bug ``get_artifacts`` was added to fix, reintroduced at the
|
|
persistence boundary. Measured in production: 0 of 265 persisted tool calls
|
|
carry ``artifacts`` while 32 of 296 ``user_logs`` rows (written from the raw
|
|
dict) do.
|
|
- ``device_id`` — the remote-device approval UI reads it to wire up its sticky
|
|
"don't ask again" action (``ConversationBubble.tsx:742-752``).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from docsgpt.agents.tool_executor import ToolExecutor
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestTruncatedToolCallsKeepsWhatTheUiNeeds:
|
|
@staticmethod
|
|
def _executor(**extra):
|
|
executor = ToolExecutor()
|
|
executor.tool_calls = [
|
|
{
|
|
"tool_name": "code_executor",
|
|
"call_id": "c1",
|
|
"action_name": "run_code",
|
|
"arguments": {"code": "..."},
|
|
"artifact_id": "a1",
|
|
"result": "ok",
|
|
"status": "completed",
|
|
**extra,
|
|
}
|
|
]
|
|
return executor
|
|
|
|
def test_keeps_every_artifact_not_just_the_first(self):
|
|
artifacts = [
|
|
{"id": "a1", "filename": "chart.png", "ref": "A1"},
|
|
{"id": "a2", "filename": "data.csv", "ref": "A2"},
|
|
]
|
|
projected = self._executor(artifacts=artifacts).get_truncated_tool_calls()
|
|
assert projected[0]["artifacts"] == artifacts
|
|
|
|
def test_keeps_the_device_id_for_the_approval_ui(self):
|
|
projected = self._executor(device_id="windows-8e15").get_truncated_tool_calls()
|
|
assert projected[0]["device_id"] == "windows-8e15"
|
|
|
|
def test_omits_the_keys_entirely_when_absent(self):
|
|
"""A plain tool call must not grow null keys in every persisted row."""
|
|
projected = self._executor().get_truncated_tool_calls()
|
|
assert "artifacts" not in projected[0]
|
|
assert "device_id" not in projected[0]
|
|
|
|
def test_still_truncates_the_result_and_drops_the_bulky_keys(self):
|
|
executor = self._executor(
|
|
result_full="x" * 100000,
|
|
resolved_arguments={"code": "y" * 100000},
|
|
)
|
|
projected = executor.get_truncated_tool_calls()
|
|
# ``result_full``/``resolved_arguments`` are deliberately not persisted:
|
|
# they are the untruncated copies this projection exists to shed.
|
|
assert "result_full" not in projected[0]
|
|
assert "resolved_arguments" not in projected[0]
|
|
|
|
def test_shape_is_otherwise_unchanged(self):
|
|
projected = self._executor().get_truncated_tool_calls()
|
|
assert set(projected[0]) == {
|
|
"tool_name",
|
|
"call_id",
|
|
"action_name",
|
|
"arguments",
|
|
"artifact_id",
|
|
"result",
|
|
"status",
|
|
}
|